Haskell中记录类型和子类型的当前状态是什么?
我知道在重载的记录名称等方面已经完成了工作。具体来说,我想制作三种不同的记录类型A
,B
和C
其中{ {1}}和B
包含与C
相同的所有字段标签,但不会相互共享字段标签。然后,我希望能够在A
,f : A -> int
,g: B -> int
处编写函数,其中函数h: C -> int
也接受类型f
和{的参数{1}}。基本上,我希望B
和C
是B
的子类型。更具体地说,如果我不必重复所有的字段标签,那就太好了。在伪代码中,这类似于
C
答案 0 :(得分:7)
没有一个。有一个建议添加重载记录字段和一个工作实现,但AFAIK还没有合并到GHC的头部。您可以阅读有关提案here的信息。一旦它落地,我们将有类似于rho多态的东西,但自动生成/推断类型类。请注意,此不是子类型。 {a :: Int, b :: Bool} <: {a :: Int}
不是Haskell中的概念,而是我们能够说出类似
foo :: r {a :: Int} -> Int
foo = a
这真的会更像是
foo :: Has "a" Int r => r -> Int
foo = a
如果我们写了类似
的内容 foo :: {a :: Int} -> {a :: Int}
foo = id
并且希望它的行为与我们的子类型一样,我们可以做类似
的事情 foo _ = A {a = 1}
并返回任何类型为{a :: Int}
的子类型。
还有像图书馆这样的替代品,比如乙烯基,在某种程度上还有镜头。我建议现在调查这些,特别是如果你想要与7.6 / 7.8兼容。
答案 1 :(得分:7)
有几种不同的方法可以实现这一点以及一些缺点。
以下是与我合作的类型:
data A = A { a :: String}
data B = B { bA :: A, b :: Char}
data C = C { cA :: A, c :: Float}
您可以将f
定义为某些CanF
类的方法,A
,B
和C
都是以下实例:
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
根据B
的实例定义C
和A
个实例,明确f
在每个实例中执行相同的操作案件。根据哪种类型定义f
实例,F
做不同的事情很容易。这种方法的缺点是需要添加任何其他类似f的函数作为相同的方法&#34; CanSomething&#34;类。
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
另一种方法是将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
在这里,你没有灵活性来定义f
可以做什么,可能是好的或坏的。优点是您可以定义其他适用于A
的函数,而无需向您的类添加新方法。
我处理此问题的首选方法是记录功能方法。定义一个参数化数据类型,其中包含要调用的函数。然后为您的记录类型定义专门的构造函数。这种方法的缺点是它通常更冗长。优点是您可以通过向F
函数提供不同的f
来换出行为。另一个优点是您可以完成更多工作而无需语言扩展。
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
答案 2 :(得分:4)
由于 HasField 类在GHC的头部分支中,使用GHC的开发版本8.2.0.20170310,我们得到了具有特定字段的记录的多态性的工作示例,可以与手写结构子类型一起使用:
{-# LANGUAGE DuplicateRecordFields, DataKinds, FlexibleContexts, TypeApplications #-}
import GHC.Records (HasField(getField))
data A = A { a :: String }
data B = B { a :: String, b :: Char }
data C = C { a :: String, c :: Float }
-- | length of field "a"
f :: HasField "a" rec String => rec -> Int
f = length . getField @"a"
main = do
print $ f $ A "a"
print $ f $ B "b" 'b'
print $ f $ C "c" 1.5
记录在“GHC 8.2分支”用户指南(https://github.com/ghc/ghc/blob/ghc-8.2/docs/users_guide/glasgow_exts.rst)中搜索“记录字段选择器多态”
来自Herbert Riedel的Ubuntu PPA的GHC 8.2.1开发二进制文件(https://launchpad.net/~hvr/+archive/ubuntu/ghc/+index?batch=150)