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 boundopt
: 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 [
"name" ==> VBytes & comment "Person's name",
"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.