The Huddle modules in Haskell provide a way to define CDDL (Concise Data Definition Language) using Haskell's higher-level capabilities. This guide, based on the provided sources, will cover the key concepts and syntax for defining CDDL in Huddle.
Huddle utilizes several core types to represent CDDL constructs: ● Huddle: The top-level type representing a collection of rules. ● HuddleItem: Represents individual items within a Huddle, such as rules, groups, or generic rules. ● Rule: A named type definition. ● Named: A type wrapper for associating a name, value, and optional description with an item. ● Value: A type representing primitive CBOR values. ● Group: Represents a collection of entries within a map or array.
Huddle makes use of some Haskell language extensions which liberalise aspects of Haskell's syntax. We recommend enabling them whenever working with Huddle:
{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE NoImplicitPrelude #-}
In addition, if using hlint, we suggest disabling the following hints:
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}
{-# HLINT ignore "Use camelCase" #-}
{-# HLINT ignore "Evaluate" #-}
Rules are defined using the =:= operator. The left-hand side of the operator is the rule name (a T.Text value), and the right-hand side is the type definition.
ruleName =:= typeDefinition
age =:= VUInt
Maps are defined using the mp function and the ==> operator to specify key-value pairs.
mapName =:= mp [ key1 ==> value1, key2 ==> value2 ]
location =:= mp [
"latitude" ==> float,
"longitude" ==> float
]
Arrays are defined using the arr function and the a function to indicate array elements.
arrayName =:= arr [ a element1, a element2 ]
point =:= arr [ a int, a int ]
Groups are collections of entries within maps or arrays. They can be named using the =:~ operator.
groupName =:~ grp [ entry1, entry2 ]
personalinfo =:~ grp [
"name" ==> tstr,
"age" ==> uint
]
Huddle represents choices between types using the / operator.
value =:= int / tstr
Huddle does not have a direct equivalent for the CDDL // operator (group choice). Instead, choices within arrays are represented by creating separate array definitions and combining them using the / operator.
optionA =:= arr [ a int, a tstr ]
optionB =:= arr [ a tstr, a int ]
choice =:= optionA / optionB
Huddle provides functions to specify occurrence quantifiers for group entries and array elements: ● <+: Lower bound ● +>: Upper bound ● opt: Optional (0 or 1 occurrences)
dat =:= arr [ 1 <+ a int +> 10 ] -- Array of 1 to 10 integers
The comment function can be used to add descriptions to rules and group entries, which will be included as comments in the generated CDDL.
person =:= comment "Represents a person" $ mp [
comment "Person's name" $ "name" ==> VBytes,
"age" ==> VUIntf
]
Huddle supports defining generic rules using the binding function.
message = binding $ \t -> "message" =:= {
"type" ==> t,
"payload" ==> any
}
The toCDDL and toCDDLNoRoot functions convert a Huddle definition to CDDL. toCDDL generates a top-level root element, while toCDDLNoRoot skips the root element.
The Conway.hs example file showcases a practical application of Huddle to define the CDDL for a specific data structure. The file defines numerous rules and groups using the Huddle syntax and functions described above.