- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在 Haskell 中,是否可以编写一个具有签名的函数,该函数可以接受两种不同(尽管相似)的数据类型,并根据传入的类型进行不同的操作?
举个例子可能会让我的问题更清楚。如果我有一个名为 myFunction
的函数,以及两个名为 MyTypeA
和 MyTypeB
的类型,我可以定义 myFunction
以便它只能接受 MyTypeA
或 MyTypeB
类型的数据作为其第一个参数?
type MyTypeA = (Int, Int, Char, Char)
type MyTypeB = ([Int], [Char])
myFunction :: MyTypeA_or_MyTypeB -> Char
myFunction constrainedToTypeA = something
myFunction constrainedToTypeB = somethingElse
在 OOP 语言中,您可以像这样编写我想要实现的目标:
public abstract class ConstrainedType {
}
public class MyTypeA extends ConstrainedType {
...various members...
}
public class MyTypeB extends ConstrainedType {
...various members...
}
...
public Char myFunction(ConstrainedType a) {
if (a TypeOf MyTypeA) {
return doStuffA();
}
else if (a TypeOf MyTypeB) {
return doStuffB();
}
}
我一直在阅读有关代数数据类型的内容,我认为我需要定义一个 Haskell 类型,但我不确定如何定义它以便它可以存储一种类型或另一个,以及我如何在自己的函数中使用它。
最佳答案
是的,你是对的,你正在寻找代数数据类型。在 Learn You a Haskell 有一个关于它们的很棒的教程。 .
郑重声明,OOP 中的抽象类概念实际上在 Haskell 中有三种不同的翻译,ADT 只是其中之一。以下是这些技术的快速概述。
代数数据类型对抽象类的模式进行编码,该抽象类的子类是已知的,并且其中的函数通过向下转换来检查该对象是哪个特定实例的成员。
abstract class IntBox { }
class Empty : IntBox { }
class Full : IntBox {
int inside;
Full(int inside) { this.inside = inside; }
}
int Get(IntBox a) {
if (a is Empty) { return 0; }
if (a is Full) { return ((Full)a).inside; }
error("IntBox not of expected type");
}
翻译成:
data IntBox = Empty | Full Int
get :: IntBox -> Int
get Empty = 0
get (Full x) = x
此样式不允许向下转换,因此上面的 Get
函数无法用此样式表达。所以这是完全不同的东西。
abstract class Animal {
abstract string CatchPhrase();
virtual void Speak() { print(CatchPhrase()); }
}
class Cat : Animal {
override string CatchPhrase() { return "Meow"; }
}
class Dog : Animal {
override string CatchPhrase() { return "Woof"; }
override void Speak() { print("Rowwrlrw"); }
}
它在 Haskell 中的翻译不会将类型映射到类型。 Animal
是唯一的类型,Dog
和 Cat
被压缩到它们的构造函数中:
data Animal = Animal {
catchPhrase :: String,
speak :: IO ()
}
protoAnimal :: Animal
protoAnimal = Animal {
speak = putStrLn (catchPhrase protoAnimal)
}
cat :: Animal
cat = protoAnimal { catchPhrase = "Meow" }
dog :: Animal
dog = protoAnimal { catchPhrase = "Woof", speak = putStrLn "Rowwrlrw" }
这个基本概念有几种不同的排列。不变的是抽象类型是记录类型,其中方法是记录的字段。
编辑:评论中对此方法的一些微妙之处进行了很好的讨论,包括上述代码中的错误。
这是我最不喜欢的 OO 思想编码。它对于 OO 程序员来说很舒服,因为它使用熟悉的单词并将类型映射到类型。但是当事情变得复杂时,上面的函数记录方法往往更容易使用。
我将再次对 Animal 示例进行编码:
class Animal a where
catchPhrase :: a -> String
speak :: a -> IO ()
speak a = putStrLn (catchPhrase a)
data Cat = Cat
instance Animal Cat where
catchPhrase Cat = "Meow"
data Dog = Dog
instance Animal Dog where
catchPhrase Dog = "Woof"
speak Dog = putStrLn "Rowwrlrw"
这看起来不错,不是吗?当您意识到尽管它看起来像面向对象,但它实际上并不像面向对象那样工作时,困难就来了。您可能想要一个动物列表,但您现在能做的最好的事情是 Animal a => [a]
,即同质动物列表,例如。仅包含猫或仅包含狗的列表。然后你需要制作这个包装类型:
{-# LANGUAGE ExistentialQuantification #-}
data AnyAnimal = forall a. Animal a => AnyAnimal a
instance Animal AnyAnimal where
catchPhrase (AnyAnimal a) = catchPhrase a
speak (AnyAnimal a) = speak a
然后 [AnyAnimal]
就是您想要的动物列表。然而,事实证明,AnyAnimal
公开了与第二个示例中的 Animal
记录完全相同相同的信息,我们刚刚讨论过以一种迂回的方式。因此,为什么我不认为类型类是一种很好的 OO 编码。
本周的《信息太多了!》就此结束!
关于haskell - 是否存在使用代数数据类型或多态性的 OOP 抽象类的 Haskell 等效项?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4029455/
我来自 Asp.Net 世界,试图理解 Angular State 的含义。 什么是 Angular 状态?它类似于Asp.Net中的ascx组件吗?是子页面吗?它类似于工作流程状态吗? 我听到很多人
我一直在寻找 3 态拨动开关,但运气不佳。 基本上我需要一个具有以下状态的开关: |开 |不适用 |关 | slider 默认从中间开始,一旦用户向左或向右滑动,就无法回到N/A(未回答)状态。 有人
我是一名优秀的程序员,十分优秀!