Как создать ADT в Haskell?

#scala #haskell #algebraic-data-types

#scala #haskell #алгебраические типы данных

Вопрос:

В Scala я могу описать такой 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
  

Как я могу сделать то же самое в Haskell?

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

Это не работает, потому что A и B не являются типами. Должен ли я использовать расширения GHC для этого?

Ответ №1:

В Scala ваш ADT делает A , B , C подтипами Foo . В Haskell у нас нет подтипов, поэтому A , B C вместо этого являются конструкторами типа Foo .

Несколько возможных обходных путей:

  1. Повторите поля. Это самый простой вариант.

     data Foo = A Int | B String | C Int String
      
  2. Определите дополнительные типы, чтобы мы могли использовать их несколько раз.

     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. Использовать 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
      

    Здесь, в последней строке, мы можем ссылаться на «значения, созданные с использованием A «, используя тип Foo 'AT , поскольку тег AT используется только конструктором A .
    Обратите внимание, что этот подход добавляет параметр тега в Foo , поэтому он немного изменяет интерфейс: мы больше не можем писать bar :: Foo -> ... , но мы должны писать bar :: Foo t -> ... (или использовать существующие типы).

Комментарии:

1. Однако для последнего примера требуется несколько расширений, верно? ( DataKinds , GADTs и KindSignatures , я думаю.)