Oop 如何在函数式语言中实现面向对象的多态性?
假设您在面向对象的应用程序中有:Oop 如何在函数式语言中实现面向对象的多态性?,oop,functional-programming,polymorphism,duck-typing,Oop,Functional Programming,Polymorphism,Duck Typing,假设您在面向对象的应用程序中有: module Talker def talk(word) puts word end end module Swimmer def swim(distance) puts "swimming #{distance}" end end class Organism def initialize rise end def rise puts "hello world&
module Talker
def talk(word)
puts word
end
end
module Swimmer
def swim(distance)
puts "swimming #{distance}"
end
end
class Organism
def initialize
rise
end
def rise
puts "hello world"
end
end
class Animal extends Organism
def think(something)
puts "think #{something}"
end
end
class Bird extends Animal
include Talker
end
class Fish extends Animal
include Swimmer
end
bird = new Bird
fish = new Fish
在这种情况下,您可以调用每个方法都是唯一的方法:
bird.talk("hello")
fish.swim(50)
但您也可以调用相同的方法:
bird.think("fly")
fish.think("swim")
如果我有一个取动物的函数,我可以调用think函数:
def experience(animal)
animal.think("one")
animal.think("two")
animal.think("one")
end
在伪函数式语言中,基本上可以执行相同的操作:
function experience(animal) {
think(animal)
think(animal)
think(animal)
}
但不是真的,您必须检查类型:
function think(genericObject) {
if (genericObject is Animal) {
animalThink(genericObject)
} else if (genericObject is SomethingElse) {
somethingElseThink(genericObject)
}
}
这是因为,在实现“体验”功能时,你不希望只让动物体验,你也希望让石头、树木和其他东西体验,但它们的体验功能是不同的
function experience(thing) {
move(thing)
move(thing)
move(thing)
}
function move(thing) {
case thing {
match Animal then animalMove(thing)
match Plant then plantMove(thing)
match Rock then rockMove(thing)
}
}
这样,您就不能拥有一个干净可重用的函数,您的函数必须知道它将在某个地方接收的特定类型
在函数式语言中,有没有办法避免这种情况并使其更像OO多态性
如果是这样的话,在高层次上,如果可以用函数式语言解决这个问题,那么它是如何工作的呢
函数式编程语言有多种实现多态性的方法。我将对比Java(我最熟悉的OOP语言)和Haskell(我最熟悉的函数式语言) 方法1:“参数多态性” 使用参数多态性,您根本不需要知道任何关于底层类型的信息。例如,如果我有一个带有T类型元素的单链接列表,我实际上不需要知道任何关于T类型的信息,就可以找到列表的长度。我会写一些像
length::对于所有a。[a] ->整数
长度[]=0
长度(x:xs)=1+长度xs
在Haskell中(显然,我想在实践中使用更好的算法,但你知道了)。请注意,列表元素的类型并不重要;获取长度的代码是相同的。第一行是“类型签名”。它表示,对于每个类型a,length都将获取a的列表并输出一个整数
这不能用于太多的“严重多态性”,但这绝对是一个良好的开端。它大致相当于Java的泛型
方式2:类型类样式多态性
甚至像检查平等性这样的良性行为实际上也需要多态性。不同的类型需要不同的代码来检查相等性,对于某些类型(通常是函数),由于停止问题,检查相等性实际上是不可能的。因此,我们使用“类型类”
假设我定义了一个包含两个元素的新类型,Bob和Larry。在哈斯克尔,这看起来像
data vegietalesstars=Bob | Larry
我希望能够比较两种元素的类型VeggieTalesStars的平等性。为此,我需要实现一个Eq实例
实例Eq VeggieTalesStars,其中
鲍勃=鲍勃=真
拉里=拉里=真
鲍勃=拉里=错
拉里=鲍勃=错
请注意,函数(=)具有类型签名
(==)::对于所有b。等式b=>b->b->Bool
这意味着对于每个类型b,如果b有一个Eq实例,那么(=)可以接受两个类型b的参数并返回Bool
猜测not equals函数(/=)也有类型签名可能不太困难
(/=)::对于所有b。等式b=>b->b->Bool
因为(/=)是由
x/=y=not(x==y)
调用(/=)函数时,该函数将根据参数的类型部署(=)函数的正确版本。如果参数具有不同的类型,则无法使用(/=)对其进行比较
Typeclass样式多态性允许您执行以下操作:
b类动物,其中
思考:b->String->String
--我们提供默认实现
think b string=“think”++string
数据鱼=鱼
数据鸟=鸟
比如动物鱼在哪里
比如动物鸟在哪里
Fish和Bird都实现了“Animal”类型类,因此我们可以在这两个类上调用think函数。就是
>>思考鸟的“思考”
“思考”
>>>想鱼“想”
“思考”
这个用例大致对应于Java接口——类型可以实现任意数量的类型类。但是类型类远比接口强大
方式3:功能
如果您的对象只有一个方法,那么它也可能只是一个函数。这是避免继承层次结构的一种非常常见的方法—处理函数而不是1方法基类的继承者
因此,我们可以定义
type Animal=String->String
动物
基本思想=“思考”++思考
“动物”实际上只是一种取一根线并产生另一根线的方式。这将对应于Java代码
类动物{
公共字符串思维(字符串思维){
返回“思考”+思考;
}
}
假设在Java中,我们决定实现animal的一个子类,如下所示:
类ThoughtfulPerson扩展动物{
私人的最后一串思想;
公众思想自由人(最后一串思想){
这个。思想=思想;
}
@凌驾
公共字符串思维(字符串思维){
System.out.println(“我通常认为“+this.think”,但我现在认为“+think+”);
}
}
在Haskell中,我们将作为
thoughtfulPerson::String->Animal
thoughtfulPerson originalthough newthough=“我通常认为“++originalthough”,但我目前认为“++newthough++”
Java代码的“依赖注入”是通过Haskell的高阶函数实现的
方式4:组合优于继承+函数
假设我们有一个带有两个方法的抽象基类:
抽象类事物{
公共抽象字符串名称();
公开摘要voi