cardano-ledger icon indicating copy to clipboard operation
cardano-ledger copied to clipboard

Make `huddleRule`, `huddleGroup` and `huddleGRule` only return the body of the definition

Open Soupstraw opened this issue 2 weeks ago • 1 comments

Currently huddleRule, huddleGroup and huddleGRule methods return the entire defnition, e.g.

instance Era era => HuddleRule "epoch" era where
  huddleRule _ = "epoch" =:= VUInt `sized` (8 :: Word64)

But we have access to the name of the rule at the type level, so I suggest to change huddleRule :: Proxy era -> Rule to huddleRuleBody :: Proxy era -> Type0 and then define huddleRule as a top-level function like so:

huddleRule :: forall (rule :: Symbol) era. (HuddleRule rule era, KnownSymbol rule) => Proxy era -> Rule
huddleRule era = symbolVal (Proxy @rule) =:= huddleRuleBody era

A similar approach can be taken for huddleGroup and huddleGRule. This approach will ensure that the name of the rule in the CDDL matches the rule in the instance, potentially saving us from future headaches.

Soupstraw avatar Dec 12 '25 11:12 Soupstraw

On a second thought, the new version of cuddle also stores the comments and custom generators in the Rule, so the method would have to provide all of that extra information as well. I think I might change the definition of Rule to newtype Rule = Rule (Named RuleBody) at some point in the future and then RuleBody would contain the extra information. Currently Type0 can't have custom generators on its own.

Soupstraw avatar Dec 12 '25 14:12 Soupstraw

the new version of cuddle also stores the comments and custom generators in the Rule

Not necessarily. We could add comment and custom generator to the HuddleRule type class, eg:

instance Era era => HuddleRule "epoch" era where
  huddleRuleBody _ = VUInt `sized` (8 :: Word64)
  huddleRuleComment _ = comment "Epoch number"
  huddleRuleGenerator _ = Nothing

Then huddleRule becomes:

huddleRule :: forall (rule :: Symbol) era. (HuddleRule rule era, KnownSymbol rule) => Proxy era -> Rule
huddleRule era = 
  comment (huddleRuleComment @rule era)
    $ maybe withGenerator id (huddleRuleGenerator @rule era)
    $ symbolVal (Proxy @rule) =:= huddleRuleBody era

lehins avatar Dec 14 '25 19:12 lehins

The original suggestion does not type-check because Type0 has many inhabitants, and in my limited understanding I could come up with two potential solutions: either something like class IsType0 r => HuddleRule name era | name era -> r or simply class IsType0 r => HuddleRule name era r, but @lehins suggested a better approach keeping the typeclass head unchanged while adding another Proxy to the huddleRuleNamed typeclass function:

class (KnownSymbol name, Era era) => HuddleRule (name :: Symbol) era where
  huddleRule :: Proxy era -> Rule
  huddleRuleNamed :: Proxy name -> Proxy era -> Rule

class (KnownSymbol name, Era era) => HuddleGroup (name :: Symbol) era where
  huddleGroup :: Proxy era -> Named Group
  huddleGroupNamed :: Proxy name -> Proxy era -> Named Group

class (KnownSymbol name, Era era) => HuddleGRule (name :: Symbol) era where
  huddleGRule :: Proxy era -> GRuleDef
  huddleGRuleNamed :: Proxy name -> Proxy era -> GRuleDef

class (KnownSymbol name, Era era) => HuddleRule1 (name :: Symbol) era where
  huddleRule1 :: IsType0 a => Proxy era -> a -> GRuleCall
  huddleRule1Named :: IsType0 a => Proxy name -> Proxy era -> a -> GRuleCall

huddleRule :: forall name era. HuddleRule name era => Proxy era -> Rule
huddleRule = huddleRuleNamed (Proxy @name)

huddleGroup :: forall name era. HuddleGroup name era => Proxy era -> Named Group
huddleGroup = huddleGroupNamed (Proxy @name)

huddleGRule :: forall name era. HuddleGRule name era => Proxy era -> GRuleDef
huddleGRule = huddleGRuleNamed (Proxy @name)

huddleRule1 :: forall name era a. (HuddleRule1 name era, IsType0 a) => Proxy era -> a -> GRuleCall
huddleRule1 = huddleRule1Named (Proxy @name)

(=.=) :: (KnownSymbol name, IsType0 t) => Proxy (name :: Symbol) -> t -> Rule
(=.=) pname t = T.pack (symbolVal pname) =:= t

infixr 0 =.=

(=.~) :: KnownSymbol name => Proxy (name :: Symbol) -> Group -> Named Group
(=.~) pname group = T.pack (symbolVal pname) =:~ group

infixr 0 =.~

aniketd avatar Dec 15 '25 10:12 aniketd

@aniketd did you try with toType0?

Soupstraw avatar Dec 15 '25 10:12 Soupstraw

class (KnownSymbol name, Era era) => HuddleRule (name :: Symbol) era where
  huddleRuleBody :: Proxy era -> Type0

instance Era era => HuddleRule "epoch" era where
  huddleRuleBody _ = toType0 (VUInt `sized` (8 :: Word64))

lehins avatar Dec 15 '25 16:12 lehins