From e7f5327009a95284091869d3945964c9f535bd0d Mon Sep 17 00:00:00 2001
From: Nicholas Clarke <nick@topos.org.uk>
Date: Mon, 20 Jan 2025 17:49:41 +0100
Subject: [PATCH 1/4] Generics, take 2

This commit adds an alternative attempt at generics. It has both
advantages and disadvantages.

The principal advantage is that it really allows generics to work with
HuddleM - see the example in example/Monad.hs for a demonstration. The
previous way of writing this was quite horrible.

There are two main disadvantages:

- Now, rather than treating a generic function as a regular Haskell
function, we have to treat it specially and call it with the special
syntax (<--).
- Only one generic parameter is supported. We can potentially fix this,
but it's always going to play unkindly with the (<--) syntax. That
having been said, we had only implemented up to two parameters before.

For these reasons, these new-style generics are currently implemented
alongside the existing ones for consideration.
---
 cuddle.cabal                            |  1 +
 example/Monad.hs                        | 13 ++--
 src/Codec/CBOR/Cuddle/Huddle.hs         | 93 +++++++++++++++++++++++--
 src/Codec/CBOR/Cuddle/Huddle/Generic.hs | 12 ++++
 src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs | 38 ++++++----
 5 files changed, 134 insertions(+), 23 deletions(-)
 create mode 100644 src/Codec/CBOR/Cuddle/Huddle/Generic.hs

diff --git a/cuddle.cabal b/cuddle.cabal
index abd23f6..bd73f51 100644
--- a/cuddle.cabal
+++ b/cuddle.cabal
@@ -51,6 +51,7 @@ library
     Codec.CBOR.Cuddle.CDDL.Prelude
     Codec.CBOR.Cuddle.CDDL.Resolve
     Codec.CBOR.Cuddle.Huddle
+    Codec.CBOR.Cuddle.Huddle.Generic
     Codec.CBOR.Cuddle.Huddle.HuddleM
     Codec.CBOR.Cuddle.Huddle.Optics
     Codec.CBOR.Cuddle.Parser
diff --git a/example/Monad.hs b/example/Monad.hs
index c009431..99ceaa1 100644
--- a/example/Monad.hs
+++ b/example/Monad.hs
@@ -19,8 +19,8 @@ spec = huddleDef $ mdo
   transaction <-
     "transaction"
       =:= mp
-        [ idx 0 ==> set txIn
-        , idx 1 ==> set txOut
+        [ idx 0 ==> set txIn,
+          idx 1 ==> set' <-- txOut
         ]
   txIn <- "txIn" =:= arr ["transaction_id" ==> hash32, "index" ==> txId]
   txOut <- "txOut" =:= arr [idx 0 ==> address, idx 1 ==> value]
@@ -29,6 +29,7 @@ spec = huddleDef $ mdo
   hash32 <- "hash32" =:= VBytes `sized` (32 :: Word64)
   value <- "value" =:= VUInt
   set <- include hdl_set
+  set' <- binding' $ \x -> "set'" Huddle.=:= arr [0 <+ a x]
 
   setRootRules [transaction]
 
@@ -37,15 +38,15 @@ spec2 =
   spec
     <> huddleDef
       ( mdo
-          set <- include hdl_set
+          set <- unsafeIncludeFromHuddle spec "set'"
           txIn <- unsafeIncludeFromHuddle spec "txIn"
           txOut <- unsafeIncludeFromHuddle spec "txOut"
           _transaction <-
             "transaction"
               =:= mp
-                [ comment "Transaction inputs" $ idx 0 ==> set txIn
-                , comment "Transaction outputs" $ idx 1 ==> set txOut
-                , comment "Metadata" $ idx 2 ==> metadata
+                [ comment "Transaction inputs" $ idx 0 ==> set <-- txIn,
+                  comment "Transaction outputs" $ idx 1 ==> set <-- txOut,
+                  comment "Metadata" $ idx 2 ==> metadata
                 ]
           metadata <- "metadata" =:= VBytes
           _value <- "value" =:= mp ["token" ==> VText, "quantity" ==> VUInt]
diff --git a/src/Codec/CBOR/Cuddle/Huddle.hs b/src/Codec/CBOR/Cuddle/Huddle.hs
index fabc883..b3547e5 100644
--- a/src/Codec/CBOR/Cuddle/Huddle.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle.hs
@@ -8,6 +8,7 @@
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE UndecidableInstances #-}
+{-# LANGUAGE ViewPatterns #-}
 {-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
 
 -- | Module for building CDDL in Haskell
@@ -76,10 +77,14 @@ module Codec.CBOR.Cuddle.Huddle (
   -- * Generics
   GRef,
   GRuleDef,
+  GRuleDef',
   GRuleCall,
+  GRuleCall',
   binding,
   binding2,
+  binding',
   callToDef,
+  (<--),
 
   -- * Conversion to CDDL
   collectFrom,
@@ -91,6 +96,7 @@ where
 import Codec.CBOR.Cuddle.CDDL (CDDL, TopLevel (..), WithComments (..))
 import Codec.CBOR.Cuddle.CDDL qualified as C
 import Codec.CBOR.Cuddle.CDDL.CtlOp qualified as CtlOp
+import Codec.CBOR.Cuddle.Huddle.Generic (FnWithArg (..), result)
 import Control.Monad (when)
 import Control.Monad.State (MonadState (get), execState, modify)
 import Data.ByteString (ByteString)
@@ -128,6 +134,7 @@ type Rule = Named Type0
 data HuddleItem
   = HIRule Rule
   | HIGRule GRuleDef
+  | HIGRule' GRuleDef'
   | HIGroup (Named Group)
   deriving (Generic, Show)
 
@@ -273,6 +280,7 @@ data Type2
   | T2Group (Named Group)
   | -- | Call to a generic rule, binding arguments
     T2Generic GRuleCall
+  | T2Generic' GRuleCall'
   | -- | Reference to a generic parameter within the body of the definition
     T2GenericRef GRef
   deriving (Show)
@@ -475,9 +483,12 @@ sized v sz =
     []
 
 class IsCborable a
+
 instance IsCborable ByteString
-instance IsCborable (AnyRef a)
-instance IsCborable GRef
+
+instance IsCborable CRef
+
+instance IsCborable CGRef
 
 cbor :: (IsCborable b, IsConstrainable c b) => c -> Rule -> Constrained
 cbor v r@(Named n _ _) =
@@ -493,9 +504,12 @@ cbor v r@(Named n _ _) =
     [r]
 
 class IsComparable a
+
 instance IsComparable Int
-instance IsComparable (AnyRef a)
-instance IsComparable GRef
+
+instance IsComparable CRef
+
+instance IsComparable CGRef
 
 le :: (IsComparable a, IsConstrainable c a) => c -> Word64 -> Constrained
 le v bound =
@@ -605,6 +619,9 @@ instance IsType0 (Named Group) where
 instance IsType0 GRuleCall where
   toType0 = NoChoice . T2Generic
 
+instance IsType0 GRuleCall' where
+  toType0 = NoChoice . T2Generic'
+
 instance IsType0 GRef where
   toType0 = NoChoice . T2GenericRef
 
@@ -617,6 +634,9 @@ instance IsType0 HuddleItem where
   toType0 (HIGRule g) =
     error $
       "Attempt to reference generic rule from HuddleItem not supported: " <> show g
+  toType0 (HIGRule' g) =
+    error $
+      "Attempt to reference generic rule from HuddleItem not supported: " <> show g
 
 class CanQuantify a where
   -- | Apply a lower bound
@@ -933,6 +953,50 @@ binding2 fRule t0 t1 =
       NoChoice x -> x
       _ -> error "Cannot use a choice of types as a generic argument"
 
+--------------------------------------------------------------------------------
+-- Generics (Take 2)
+--------------------------------------------------------------------------------
+
+type GRuleDef' = Named (FnWithArg GRef Type0)
+
+data GRuleCallAux = GRuleCallAux
+  { defFn :: FnWithArg GRef Type0,
+    callArg :: Type2
+  }
+
+type GRuleCall' = Named GRuleCallAux
+
+binding' :: (GRef -> Rule) -> GRuleDef'
+binding' fRule =
+  Named
+    (getField @"name" $ result defFn)
+    (getField @"value" <$> defFn)
+    Nothing
+  where
+    defFn = FnWithArg fRule (freshName 0)
+
+class IsGRuleDef f where
+  toGRuleDef :: f -> GRuleDef'
+
+instance IsGRuleDef GRuleDef' where
+  toGRuleDef = id
+
+instance IsGRuleDef HuddleItem where
+  toGRuleDef (HIGRule' gd) = gd
+  toGRuleDef _ = error "Attempt to use a non-generic rule as a GRuleDef"
+
+(<--) :: (IsType0 t0, IsGRuleDef gd) => gd -> t0 -> GRuleCall'
+(toGRuleDef -> f) <-- t0 = fmap toCall f
+  where
+    toCall rd =
+      GRuleCallAux
+        { defFn = rd,
+          callArg = t2
+        }
+    t2 = case toType0 t0 of
+      NoChoice x -> x
+      _ -> error "Cannot use a choice of types as a generic argument"
+
 --------------------------------------------------------------------------------
 -- Collecting all top-level rules
 --------------------------------------------------------------------------------
@@ -1022,6 +1086,7 @@ toCDDL' mkPseudoRoot hdl =
     toCDDLItem (HIRule r) = toCDDLRule r
     toCDDLItem (HIGroup g) = toCDDLGroup g
     toCDDLItem (HIGRule g) = toGenRuleDef g
+    toCDDLItem (HIGRule' g) = toGenRuleDef' g
     toTopLevelPseudoRoot :: [Rule] -> C.WithComments C.Rule
     toTopLevelPseudoRoot topRs =
       toCDDLRule $
@@ -1084,6 +1149,7 @@ toCDDL' mkPseudoRoot hdl =
       T2Ref (Named n _ _) -> C.Type1 (C.T2Name (C.Name n) Nothing) Nothing
       T2Group (Named n _ _) -> C.Type1 (C.T2Name (C.Name n) Nothing) Nothing
       T2Generic g -> C.Type1 (toGenericCall g) Nothing
+      T2Generic' g -> C.Type1 (toGenericCall' g) Nothing
       T2GenericRef (GRef n) -> C.Type1 (C.T2Name (C.Name n) Nothing) Nothing
 
     toMemberKey :: Key -> C.MemberKey
@@ -1158,6 +1224,12 @@ toCDDL' mkPseudoRoot hdl =
         (C.Name n)
         (Just . C.GenericArg $ fmap toCDDLType1 (args gr))
 
+    toGenericCall' :: GRuleCall' -> C.Type2
+    toGenericCall' (Named n gr _) =
+      C.T2Name
+        (C.Name n)
+        (Just . C.GenericArg $ NE.singleton (toCDDLType1 (callArg gr)))
+
     toGenRuleDef :: GRuleDef -> C.WithComments C.Rule
     toGenRuleDef (Named n gr c) =
       C.WithComments
@@ -1170,3 +1242,16 @@ toCDDL' mkPseudoRoot hdl =
       where
         gps =
           C.GenericParam $ fmap (\(GRef t) -> C.Name t) (args gr)
+
+    toGenRuleDef' :: GRuleDef' -> C.WithComments C.Rule
+    toGenRuleDef' (Named n g c) =
+      C.WithComments
+        ( C.Rule (C.Name n) (Just gps) C.AssignEq
+            . C.TOGType
+            $ C.Type0
+            $ toCDDLType1 <$> choiceToNE (fn g (arg g))
+        )
+        (C.comment <$> c)
+      where
+        gps =
+          C.GenericParam $ fmap (\(GRef t) -> C.Name t) (NE.singleton $ arg g)
diff --git a/src/Codec/CBOR/Cuddle/Huddle/Generic.hs b/src/Codec/CBOR/Cuddle/Huddle/Generic.hs
new file mode 100644
index 0000000..e1e48d5
--- /dev/null
+++ b/src/Codec/CBOR/Cuddle/Huddle/Generic.hs
@@ -0,0 +1,12 @@
+module Codec.CBOR.Cuddle.Huddle.Generic where
+
+-- | Function carrying its argument
+data FnWithArg a result = FnWithArg
+  { fn :: a -> result,
+    arg :: a
+  }
+  deriving (Functor)
+
+-- | Evaluate a function carrying its argument to its result
+result :: FnWithArg a result -> result
+result a = fn a (arg a)
diff --git a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
index 6cec70e..536685d 100644
--- a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
@@ -1,19 +1,20 @@
 -- | Monad for declaring Huddle constructs
-module Codec.CBOR.Cuddle.Huddle.HuddleM (
-  module Huddle,
-  (=:=),
-  (=:~),
-  (=::=),
-  binding,
-  setRootRules,
-  huddleDef,
-  huddleDef',
-  include,
-  unsafeIncludeFromHuddle,
-)
+module Codec.CBOR.Cuddle.Huddle.HuddleM
+  ( module Huddle,
+    (=:=),
+    (=:~),
+    (=::=),
+    binding,
+    binding',
+    setRootRules,
+    huddleDef,
+    huddleDef',
+    include,
+    unsafeIncludeFromHuddle,
+  )
 where
 
-import Codec.CBOR.Cuddle.Huddle hiding (binding, (=:=), (=:~))
+import Codec.CBOR.Cuddle.Huddle hiding (binding, binding', (=:=), (=:~))
 import Codec.CBOR.Cuddle.Huddle qualified as Huddle
 import Control.Monad.State.Strict (State, modify, runState)
 import Data.Default.Class (def)
@@ -43,6 +44,11 @@ binding ::
   HuddleM (t0 -> GRuleCall)
 binding fRule = include (Huddle.binding fRule)
 
+binding' ::
+  (GRef -> Rule) ->
+  HuddleM GRuleDef'
+binding' fRule = include (Huddle.binding' fRule)
+
 -- | Renamed version of Huddle's underlying '=:=' for use in generic bindings
 (=::=) :: IsType0 a => T.Text -> a -> Rule
 n =::= b = n Huddle.=:= b
@@ -84,9 +90,15 @@ instance IsType0 t0 => Includable (t0 -> GRuleCall) where
           modify (field @"items" %~ (OMap.|> (n, HIGRule grDef)))
           pure gr
 
+instance Includable GRuleDef' where
+  include r =
+    modify (field @"items" %~ (OMap.|> (r ^. field @"name", HIGRule' r)))
+      >> pure r
+
 instance Includable HuddleItem where
   include x@(HIRule r) = include r >> pure x
   include x@(HIGroup g) = include g >> pure x
+  include x@(HIGRule' g) = include g >> pure x
   include x@(HIGRule g) =
     let n = g ^. field @"name"
      in do

From c4afe6401484e020a55ef009b3ee24faef1bd54a Mon Sep 17 00:00:00 2001
From: Nicholas Clarke <nick@topos.org.uk>
Date: Tue, 21 Jan 2025 09:40:47 +0100
Subject: [PATCH 2/4] Fix compilation in GHC 8.10.7

NE.singleton doesn't exist until a later version of base.
---
 src/Codec/CBOR/Cuddle/Huddle.hs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Codec/CBOR/Cuddle/Huddle.hs b/src/Codec/CBOR/Cuddle/Huddle.hs
index b3547e5..75a4f3d 100644
--- a/src/Codec/CBOR/Cuddle/Huddle.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle.hs
@@ -1228,7 +1228,7 @@ toCDDL' mkPseudoRoot hdl =
     toGenericCall' (Named n gr _) =
       C.T2Name
         (C.Name n)
-        (Just . C.GenericArg $ NE.singleton (toCDDLType1 (callArg gr)))
+        (Just . C.GenericArg $ (toCDDLType1 (callArg gr)) NE.:| [])
 
     toGenRuleDef :: GRuleDef -> C.WithComments C.Rule
     toGenRuleDef (Named n gr c) =
@@ -1254,4 +1254,4 @@ toCDDL' mkPseudoRoot hdl =
         (C.comment <$> c)
       where
         gps =
-          C.GenericParam $ fmap (\(GRef t) -> C.Name t) (NE.singleton $ arg g)
+          C.GenericParam $ fmap (\(GRef t) -> C.Name t) (arg g NE.:| [])

From 978e927c178e4d71d34db39b37f608a560dc2bf7 Mon Sep 17 00:00:00 2001
From: Nicholas Clarke <nick@topos.org.uk>
Date: Wed, 22 Jan 2025 09:50:24 +0100
Subject: [PATCH 3/4] Improve comments and error messages

---
 example/Monad.hs                        | 10 ++++----
 src/Codec/CBOR/Cuddle/Huddle.hs         | 32 +++++++++++++++----------
 src/Codec/CBOR/Cuddle/Huddle/Generic.hs |  4 ++--
 src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs | 26 ++++++++++----------
 4 files changed, 40 insertions(+), 32 deletions(-)

diff --git a/example/Monad.hs b/example/Monad.hs
index 99ceaa1..626c9c2 100644
--- a/example/Monad.hs
+++ b/example/Monad.hs
@@ -19,8 +19,8 @@ spec = huddleDef $ mdo
   transaction <-
     "transaction"
       =:= mp
-        [ idx 0 ==> set txIn,
-          idx 1 ==> set' <-- txOut
+        [ idx 0 ==> set txIn
+        , idx 1 ==> set' <-- txOut
         ]
   txIn <- "txIn" =:= arr ["transaction_id" ==> hash32, "index" ==> txId]
   txOut <- "txOut" =:= arr [idx 0 ==> address, idx 1 ==> value]
@@ -44,9 +44,9 @@ spec2 =
           _transaction <-
             "transaction"
               =:= mp
-                [ comment "Transaction inputs" $ idx 0 ==> set <-- txIn,
-                  comment "Transaction outputs" $ idx 1 ==> set <-- txOut,
-                  comment "Metadata" $ idx 2 ==> metadata
+                [ comment "Transaction inputs" $ idx 0 ==> set <-- txIn
+                , comment "Transaction outputs" $ idx 1 ==> set <-- txOut
+                , comment "Metadata" $ idx 2 ==> metadata
                 ]
           metadata <- "metadata" =:= VBytes
           _value <- "value" =:= mp ["token" ==> VText, "quantity" ==> VUInt]
diff --git a/src/Codec/CBOR/Cuddle/Huddle.hs b/src/Codec/CBOR/Cuddle/Huddle.hs
index 75a4f3d..13d91e2 100644
--- a/src/Codec/CBOR/Cuddle/Huddle.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle.hs
@@ -486,9 +486,9 @@ class IsCborable a
 
 instance IsCborable ByteString
 
-instance IsCborable CRef
+instance IsCborable CRefType
 
-instance IsCborable CGRef
+instance IsCborable CGRefType
 
 cbor :: (IsCborable b, IsConstrainable c b) => c -> Rule -> Constrained
 cbor v r@(Named n _ _) =
@@ -507,9 +507,9 @@ class IsComparable a
 
 instance IsComparable Int
 
-instance IsComparable CRef
+instance IsComparable CRefType
 
-instance IsComparable CGRef
+instance IsComparable CGRefType
 
 le :: (IsComparable a, IsConstrainable c a) => c -> Word64 -> Constrained
 le v bound =
@@ -633,10 +633,14 @@ instance IsType0 HuddleItem where
   toType0 (HIGroup g) = toType0 g
   toType0 (HIGRule g) =
     error $
-      "Attempt to reference generic rule from HuddleItem not supported: " <> show g
+      "Attempt to reference a raw generic rule: "
+        <> show g
+        <> ". Most likely this indicates you haven't provided generic parameters."
   toType0 (HIGRule' g) =
     error $
-      "Attempt to reference generic rule from HuddleItem not supported: " <> show g
+      "Attempt to reference a raw generic rule: "
+        <> show g
+        <> ". Most likely this indicates you haven't provided generic parameters."
 
 class CanQuantify a where
   -- | Apply a lower bound
@@ -960,12 +964,13 @@ binding2 fRule t0 t1 =
 type GRuleDef' = Named (FnWithArg GRef Type0)
 
 data GRuleCallAux = GRuleCallAux
-  { defFn :: FnWithArg GRef Type0,
-    callArg :: Type2
+  { defFn :: FnWithArg GRef Type0
+  , callArg :: Type2
   }
 
 type GRuleCall' = Named GRuleCallAux
 
+-- | Create a generic rule definition binding a single generic parameter.
 binding' :: (GRef -> Rule) -> GRuleDef'
 binding' fRule =
   Named
@@ -983,15 +988,18 @@ instance IsGRuleDef GRuleDef' where
 
 instance IsGRuleDef HuddleItem where
   toGRuleDef (HIGRule' gd) = gd
-  toGRuleDef _ = error "Attempt to use a non-generic rule as a GRuleDef"
+  toGRuleDef hi =
+    error $
+      "Attempt to apply generic parameters to a non-generic rule: " <> show hi
 
+-- | Call a generic definition, applying the given type parameter.
 (<--) :: (IsType0 t0, IsGRuleDef gd) => gd -> t0 -> GRuleCall'
 (toGRuleDef -> f) <-- t0 = fmap toCall f
   where
     toCall rd =
       GRuleCallAux
-        { defFn = rd,
-          callArg = t2
+        { defFn = rd
+        , callArg = t2
         }
     t2 = case toType0 t0 of
       NoChoice x -> x
@@ -1228,7 +1236,7 @@ toCDDL' mkPseudoRoot hdl =
     toGenericCall' (Named n gr _) =
       C.T2Name
         (C.Name n)
-        (Just . C.GenericArg $ (toCDDLType1 (callArg gr)) NE.:| [])
+        (Just . C.GenericArg $ toCDDLType1 (callArg gr) NE.:| [])
 
     toGenRuleDef :: GRuleDef -> C.WithComments C.Rule
     toGenRuleDef (Named n gr c) =
diff --git a/src/Codec/CBOR/Cuddle/Huddle/Generic.hs b/src/Codec/CBOR/Cuddle/Huddle/Generic.hs
index e1e48d5..36fea22 100644
--- a/src/Codec/CBOR/Cuddle/Huddle/Generic.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle/Generic.hs
@@ -2,8 +2,8 @@ module Codec.CBOR.Cuddle.Huddle.Generic where
 
 -- | Function carrying its argument
 data FnWithArg a result = FnWithArg
-  { fn :: a -> result,
-    arg :: a
+  { fn :: a -> result
+  , arg :: a
   }
   deriving (Functor)
 
diff --git a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
index 536685d..66c5b23 100644
--- a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
@@ -1,17 +1,17 @@
 -- | Monad for declaring Huddle constructs
-module Codec.CBOR.Cuddle.Huddle.HuddleM
-  ( module Huddle,
-    (=:=),
-    (=:~),
-    (=::=),
-    binding,
-    binding',
-    setRootRules,
-    huddleDef,
-    huddleDef',
-    include,
-    unsafeIncludeFromHuddle,
-  )
+module Codec.CBOR.Cuddle.Huddle.HuddleM (
+  module Huddle,
+  (=:=),
+  (=:~),
+  (=::=),
+  binding,
+  binding',
+  setRootRules,
+  huddleDef,
+  huddleDef',
+  include,
+  unsafeIncludeFromHuddle,
+)
 where
 
 import Codec.CBOR.Cuddle.Huddle hiding (binding, binding', (=:=), (=:~))

From 6f7750ba100874ff6a7c59f12c5d7de146ac0bc7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Joosep=20J=C3=A4=C3=A4ger?= <joosep.jaager@iohk.io>
Date: Tue, 15 Apr 2025 17:30:47 +0300
Subject: [PATCH 4/4] Changed fixity of (=:=) and (=:~) to be same as (==)

---
 src/Codec/CBOR/Cuddle/Huddle.hs         | 4 ++--
 src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/Codec/CBOR/Cuddle/Huddle.hs b/src/Codec/CBOR/Cuddle/Huddle.hs
index 13d91e2..bb4f960 100644
--- a/src/Codec/CBOR/Cuddle/Huddle.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle.hs
@@ -708,12 +708,12 @@ infixl 8 ==>
 (=:=) :: IsType0 a => T.Text -> a -> Rule
 n =:= b = Named n (toType0 b) Nothing
 
-infixl 1 =:=
+infixl 4 =:=
 
 (=:~) :: T.Text -> Group -> Named Group
 n =:~ b = Named n b Nothing
 
-infixl 1 =:~
+infixl 4 =:~
 
 class IsGroupOrArrayEntry a where
   toGroupOrArrayEntry :: IsType0 x => x -> a
diff --git a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
index 66c5b23..71962ca 100644
--- a/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
+++ b/src/Codec/CBOR/Cuddle/Huddle/HuddleM.hs
@@ -29,13 +29,13 @@ type HuddleM = State Huddle
 (=:=) :: IsType0 a => T.Text -> a -> HuddleM Rule
 n =:= b = let r = n Huddle.=:= b in include r
 
-infixl 1 =:=
+infixl 4 =:=
 
 -- | Overridden version of group assignment which adds the rule to the state
 (=:~) :: T.Text -> Group -> HuddleM (Named Group)
 n =:~ b = let r = n Huddle.=:~ b in include r
 
-infixl 1 =:~
+infixl 4 =:~
 
 binding ::
   forall t0.