Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Oop 如何在函数式语言中实现面向对象的多态性?_Oop_Functional Programming_Polymorphism_Duck Typing - Fatal编程技术网

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