61828

Extensible records (I think)

Question:

What I roughly want is this:

data A = ... data B = ... data C = ... class HasA t where getA :: t -> A class HasB t where getB :: t -> B class HasC t where getC :: t -> C

So I can do something like this (pseudocode follows):

a :: A b :: B x = mkRecord { elemA a, elemB b } y = mkRecord { elemB b, elemA a } -- type of `x` == type of `y`

Naturally, only the appropriate get functions work, in the above case getA and getB.

I'd also like the following functions

slice :: Subset a b => a -> b slice x = -- just remove the bits of x that aren't in type b. add :: e -> a -> a ++ e add e x = -- add an element to the "record" (compile error if it's already there)

I feel like this is not a new problem so perhaps a resolution for this already exists. Note that I don't require the solution to be extensible, the amount of types I need to deal with is finite and known, but of course and extensible one wouldn't hurt.

I've found a couple of packages that seem to be in the field of what I'm looking for, namely <a href="https://hackage.haskell.org/package/HList" rel="nofollow">HList</a> and <a href="https://hackage.haskell.org/package/extensible" rel="nofollow">extensible</a> (perhaps extensible is better because I want my records unordered). I got a bit lost in the Hackage docs so I'd like just some sample code (or a link to some sample code) that roughly achieves what I'm looking for.

Answer1:

This is exactly what HList is good for. However, since I don't have the right setup to test something with the HList package right now (and besides, it has <a href="https://stackoverflow.com/q/41135212/3072788" rel="nofollow">more confusing data definitions</a>), here is a minimal example of HList that uses singletons for the type-level list stuff.

{-# LANGUAGE DataKinds, TypeOperators, GADTs,TypeFamilies, UndecidableInstances, PolyKinds, FlexibleInstances, MultiParamTypeClasses #-} import Data.Singletons import Data.Promotion.Prelude.List data HList (l :: [*]) where HNil :: HList '[] HCons :: x -> HList xs -> HList (x ': xs)

The add function is the simplest: it is just HCons:

add :: x -> HList xs -> HList (x ': xs) add = HCons

Something more interesting is combining two records:

-- Notice we are using `:++` from singletons combine :: HList xs -> HList ys -> HList (xs :++ ys) combine HNil xs = xs combine (x `HCons` xs) ys = x `HCons` (xs `combine` ys)

Now, for your get function, you need to dispatch based on the type-level list. To do this, you need an overlapping type class.

class Has x xs where get :: xs -> x instance {-# OVERLAPS #-} Has x (HList (x ': xs)) where get (x `HCons` _) = x instance Has x (HList xs) => Has x (HList (y ': xs)) where get (_ `HCons` xs) = get xs

Finally, we can use Has to define a similar Subset class. Same idea as before.

class Subset ys xs where slice :: xs -> ys instance Subset (HList '[]) (HList xs) where slice _ = HNil instance (Get y (HList xs), Subset (HList ys) (HList xs)) => Subset (HList (y ': ys)) (HList xs) where slice xs = get xs `HCons` slice xs <hr />

As you mention in parens, the simple HList form does not ensure you have only <em>one</em> of any type of field (so get just returns the first field, ignoring the rest). If you want uniqueness, you can just add a constraint to the HList constructor.

data Record (l :: [*]) where Nil :: Record '[] Cons :: (NotElem x xs ~ 'True) => x -> Record xs -> Record (x ': xs)

However, defining Subset using Record looks like it involves some proofs. :)

Recommend

  • Including LaTeX symbols with MathJax inside Google Charts?
  • Global bookmarks in Delphi editor
  • Enabling button for a limited time in android app
  • Using dateadd in django filter
  • Make rectangle overlay image with canvas
  • AdMob interstitial ad only shown once
  • Error NU1605 Detected package downgrade
  • iPad launch image problem
  • SQLSTATE[HY000] [1045] exception while connecting to database using 000webhost
  • Paypal IPN Listener Issue in C#
  • spring-data-redis Jackson serialization
  • How to save a image coded in Data-URI?
  • implementing euclidean distance based formula using numpy
  • Why context.Wait in StartAsync didn't stop the dialog
  • How to scroll the horizontal scrollbar in an iFrame from the parent frame?
  • Is there way to structure a QueryExpression so that you could dynamically handle a unknown number of
  • Button On Click event not firing
  • Unknown type name with typedef struct in C
  • Difference between assigning instantiation to parent class and derived class
  • Raphael-GWT: fill image of a shape (Rect) appears offset. How to resolve this?
  • Magento Layered Navigation block. Move to center
  • Unable to connect to Azure MySQL Database through Azure Function - C#
  • How to get the Owner of the ContextMenu (from Silverlight 4 toolkit)?
  • JQuery Mobile Ajax Navigation in Single-Page Template
  • view details for exception in vs 2017
  • How to add html image in to velocity template file to send email?
  • Facebook Error (#200) The user hasn't authorized the application to perform this action (PHP)
  • How to load dynamic images in custom ListView
  • What is the difference between dynamically creating a script tag and statically embed a script tag?
  • Java .policy file - how to prevent java.util.Date() from being accessible
  • How to merge objects within array based on attribute
  • concise way of flattening multiindex columns
  • Unable to create Access token grant type in wso2 API manager store to test API
  • How to use Streams api peek() function and make it work?
  • How to mutate multiple variables without repeating codes?