Ser*_*nin 6 haskell existential-type
我试图使用Haskell/Existentially quantified types 一文来掌握 Haskell 中存在类型的概念。乍一看,这个概念似乎很清楚,有点类似于面向对象语言中的泛型。主要的例子有一个叫做“异构列表”的东西,定义如下:
data ShowBox = forall s. Show s => SB s
heteroList :: [ShowBox]
heteroList = [SB (), SB 5, SB True]
instance Show ShowBox where
show (SB s) = show s
f :: [ShowBox] -> IO ()
f xs = mapM_ print xs
main = f heteroList
Run Code Online (Sandbox Code Playgroud)
我对“异构列表”有不同的概念,类似于Scala 中的 Shapeless。但在这里,它只是一个包含在仅添加类型约束的存在类型中的项目列表。它的元素的确切类型并没有体现在它的类型签名中,我们唯一知道的是它们都符合类型约束。
在面向对象的语言中,写这样的东西似乎很自然(Java 中的例子)。这是一个无处不在的用例,我不需要创建包装器类型来处理所有实现某个接口的对象列表。该animals列表中有一个泛型类型List<Vocal>,因此我可以假设它的元素都符合这个Vocal接口:
interface Vocal {
void voice();
}
class Cat implements Vocal {
public void voice() {
System.out.println("meow");
}
}
class Dog implements Vocal {
public void voice() {
System.out.println("bark");
}
}
var animals = Arrays.asList(new Cat(), new Dog());
animals.forEach(Vocal::voice);
Run Code Online (Sandbox Code Playgroud)
我注意到存在类型仅可用作语言扩展,并且在大多数“基本”Haskell 书籍或教程中都没有描述它们,所以我的建议是这是一个非常高级的语言功能。
我的问题是,为什么?在具有泛型的语言中看起来很基本的东西(构造和使用其类型实现某些接口并以多态方式访问它们的对象列表),在 Haskell 中需要语言扩展、自定义语法并创建额外的包装器类型?有没有办法在不使用存在类型的情况下实现类似的东西,或者只是没有基本级别的用例?
或者也许我只是混淆了概念,存在类型和泛型意味着完全不同的东西。请帮助我理解它。
是的,存在类型和泛型意味着不同的东西。存在类型的使用类似于面向对象语言中的接口。您当然可以将一个放在列表中,但是使用接口不需要列表或任何其他泛型类型。有一个类型的变量Vocal来演示它的用法就足够了。
它在 Haskell 中没有被广泛使用,因为大多数时候它并不是真正需要的。
nonHeteroList :: [IO ()]
nonHeteroList = [print (), print 5, print True]
Run Code Online (Sandbox Code Playgroud)
在没有任何语言扩展的情况下做同样的事情。
存在类型(或面向对象语言中的接口)只不过是带有捆绑的方法字典的一段数据。如果您的字典中只有一种方法,请使用函数。如果您有多个,则可以使用元组或这些元组的记录。所以如果你有类似的东西
interface Shape {
void Draw();
double Area();
}
Run Code Online (Sandbox Code Playgroud)
你可以用 Haskell 来表达,例如,
type Shape = (IO (), Double)
Run Code Online (Sandbox Code Playgroud)
说
circle center radius = (drawCicrle center radius, pi * radius * radius)
rectangle topLeft bottomRight = (drawRectangle topLeft bottomRight,
abs $ (topLeft.x-bottomRight.x) * (topLeft.y-bottomRight.y))
shapes = [circe (P 2.0 3.5) 4.2, rectangle (P 3.3 7.2) (P -2.0 3.1)]
Run Code Online (Sandbox Code Playgroud)
虽然你可以用类型类、实例和存在来表达完全相同的东西
class Shape a where
draw :: a -> IO ()
area :: a -> Double
data ShapeBox = forall s. Shape s => SB s
instance Shape ShapeBox where
draw (SB a) = draw a
area (SB a) = area a
data Circle = Circle Point Double
instance Shape Circle where
draw (Circle c r) = drawCircle c r
area (Circle _ r) = pi * r * r
data Rectangle = Rectangle Point Point
instance Shape Rectangle where
draw (Rectangle tl br) = drawRectangle tl br
area (Rectangle tl br) = abs $ (tl.x - br.x) * (tl.y - br.y)
shapes = [Circle (P 2.0 3.5) 4.2, Rectangle (P 3.3 7.2) (P -2.0 3.1)]
Run Code Online (Sandbox Code Playgroud)
有了它,时间长了 N 倍。