#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
.
Несколько возможных обходных путей:
-
Повторите поля. Это самый простой вариант.
data Foo = A Int | B String | C Int String
-
Определите дополнительные типы, чтобы мы могли использовать их несколько раз.
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
-
Использовать 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
, я думаю.)