Haskell中记录类型和子类型的当前状态

wye*_*r33 4 haskell

Haskell中记录类型和子类型的当前状态是什么?

我知道有一个关于事情做过类似超载记录名称等具体工作了,我想提出三个不同的记录类型A,B以及C在那里BC包含所有相同的字段标签作为A,但不分享彼此的字段标签.然后,我希望能写的功能,其中f : A -> int,g: B -> int,h: C -> int其中功能f还接受带有类型的参数BC.基本上,我想要BC成为亚型A.更具体地说,如果我不必重复所有的字段标签,那就太好了.在伪代码中,这就像

data A = A { a :: String }
data B = B { A, b :: Char }
data C = C { C, c :: Float }

f :: A/B/C -> int
g :: B -> int
h :: C -> int
Run Code Online (Sandbox Code Playgroud)

Dan*_*zer 7

没有一个.有一个建议添加重载记录字段和一个工作实现,但AFAIK还没有合并到GHC的头部.您可以在此处阅读该提案.一旦它落地,我们将有类似于rho多态的东西,但自动生成/推断类型类.请注意,这不是子类型.{a :: Int, b :: Bool} <: {a :: Int}在Haskell中不是一个概念,而是我们能够说出类似的东西

 foo :: r {a :: Int} -> Int
 foo = a
Run Code Online (Sandbox Code Playgroud)

这真的会更像

 foo :: Has "a" Int r => r -> Int
 foo = a
Run Code Online (Sandbox Code Playgroud)

相反,我们写了类似的东西

 foo :: {a :: Int} -> {a :: Int}
 foo = id
Run Code Online (Sandbox Code Playgroud)

并希望这表现得像我们的子类型,我们可以做类似的事情

 foo _ = A {a = 1}
Run Code Online (Sandbox Code Playgroud)

并返回任何类型的子类型{a :: Int}.

库存有其他选择,如乙烯基,在某种程度上还有镜头.我建议现在调查这些,特别是如果你想要与7.6/7.8兼容.


Mic*_*ele 7

有几种不同的方法来实现这一点以及一些缺点.

以下是我正在使用的类型:

data A = A { a :: String}
data B = B { bA :: A, b :: Char}
data C = C { cA :: A, c :: Float}
Run Code Online (Sandbox Code Playgroud)

Ad-hoc多态性:所有这些类型都可以

您可以定义f一些的方法CanFA,BC所有的实例:

class CanF a where
  f :: a -> Int

instance CanF A where
  f = length . a

instance CanF B where
  f = f . bA

instance CanF C where
  f = f . cA
Run Code Online (Sandbox Code Playgroud)

根据实例定义BC实例A清楚地表明f在每种情况下做同样的事情.f根据定义F实例的类型,很容易做出不同的事情.这种方法的缺点是需要添加任何其他类似f的函数作为相同"CanSomething"类的方法.

main :: IO ()
main = do
    print (f a)
    print (f b)
    print (f c)
  where
    a = A "Hello"
    b = B a 'H'
    c = C a 3.14
Run Code Online (Sandbox Code Playgroud)

Ad-hoc多态性:所有这些类型都可以表示为A.

另一种方法是编写f一个由一个总是给你一个类的类约束的函数A.

class RepA a where
  getA :: a -> A

instance RepA A where
  getA = id

instance RepA B where
  getA = bA

instance RepA C where
  getA = cA

f :: RepA a => a -> Int
f = length . a . getA
Run Code Online (Sandbox Code Playgroud)

在这里,您没有灵活性来定义f可以做什么,哪些可能好或坏.优点是您可以定义其他功能,A而不需要为您的类添加新方法.

功能记录

处理此问题的首选方法是记录功能方法.定义一个参数化数据类型,其中包含要调用的函数.然后为您的记录类型定义专门的构造函数.这种方法的缺点是它通常更冗长.优点是您可以通过提供不同Ff功能来换出行为.另一个优点是您无需语言扩展即可完成更多任务.

data F a = F { f :: a -> Int }

af :: F A
af = F $ length . a

bf :: F B
bf = F $ f af . bA

cf :: F C
cf = F $ f af . cA

main :: IO ()
main = do
    print (f af a)
    print (f bf b)
    print (f cf c)
  where
    a = A "Hello"
    b = B a 'H'
    c = C a 3.14
Run Code Online (Sandbox Code Playgroud)