Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create ADT in Haskell?

In Scala I can describe such ADT:

sealed trait Foo
case class A(a: Int) extends Foo
case class B(b: String) extends Foo
case class C(a: A, b: B) extends Foo

How can I do the same in Haskell?

data Foo = A Int | B String | C A B

It doesn't work, because A and B are not types. Should I use GHC extensions to do it?

like image 251
Andrew Avatar asked Mar 23 '19 08:03

Andrew


People also ask

What is ADT in Haskell?

Definition. An abstract data type is a type with associated operations, but whose representation is hidden. Common examples of abstract data types are the built-in primitive types in Haskell, Integer and Float. Haskell supports the definition of abstract data types via the module system.

What is an algebraic data type in Haskell?

Haskell. Leaving pets aside, probably the most common example of algebraic data type in Haskell is a Tree , which describes elements of a tree-like data structure. A Tree elements might come in one of two forms: a node, which contains a value, together with its left and right “descendants” (or nested tree elements)

What is Newtype in Haskell?

In Haskell, the newtype declaration creates a new type from an existing one. For example, natural numbers can be represented by the type Integer using the following declaration: newtype Natural = MakeNatural Integer. This creates an entirely new type, Natural, whose only constructor contains a single Integer.


1 Answers

In Scala, your ADT makes A,B,C to be subtypes of Foo. In Haskell we do not have subtypes, so A,B,C are instead constructors of type Foo.

A few possible workarounds:

  1. Repeat the fields. This is the most basic option.

    data Foo = A Int | B String | C Int String
    
  2. Define additional types, so that we can reuse them more than once.

    data AT = AT Int      -- can have many arguments
    data BT = BT String   -- can have many arguments
    data Foo = A AT | B BT | C AT BT
    
  3. Exploit a GADT

    data FooTag = AT | BT | CT
    
    data Foo (tag :: FooTag) where
       A :: Int -> Foo 'AT
       B :: String -> Foo 'BT
       C :: Foo 'AT -> Foo 'BT -> Foo 'CT
    

    Here, in the last line we are able to refer to "the values constructed using A" using the type Foo 'AT, since tag AT is only used by constructor A. Note that this approach adds a tag parameter to Foo, so it slightly changes the interface: we can no longer write bar :: Foo -> ..., but we have to write bar :: Foo t -> ... (or to use existential types).

like image 189
chi Avatar answered Oct 06 '22 08:10

chi