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 在Haskell中创建绑定到记录的方法_Oop_Haskell_Types_Polymorphism_Higher Rank Types - Fatal编程技术网

Oop 在Haskell中创建绑定到记录的方法

Oop 在Haskell中创建绑定到记录的方法,oop,haskell,types,polymorphism,higher-rank-types,Oop,Haskell,Types,Polymorphism,Higher Rank Types,我正在创建一个懒惰的函数式结构,它允许用户使用方法定义不可变的结构(类似于OO语言中的类,但它们是不可变的)。我把这种语言的代码编译成Haskell代码 最近我遇到了这个工作流的一个问题。我不想强迫用户编写显式类型,所以我想大量使用Haskell的类型推断器。当我翻译一个函数时会出现问题,该函数多次调用“对象”的多态方法,每次传递不同的参数类型,如下所示: (伪代码): X类{ def方法1(a、b){ (a,b)//返回 } } def f(x){ print(x.method1(1,2))/

我正在创建一个懒惰的函数式结构,它允许用户使用方法定义不可变的结构(类似于OO语言中的类,但它们是不可变的)。我把这种语言的代码编译成Haskell代码

最近我遇到了这个工作流的一个问题。我不想强迫用户编写显式类型,所以我想大量使用Haskell的类型推断器。当我翻译一个函数时会出现问题,该函数多次调用“对象”的多态方法,每次传递不同的参数类型,如下所示:

(伪代码):

X类{
def方法1(a、b){
(a,b)//返回
}
}
def f(x){
print(x.method1(1,2))//使用int调用method1
print(x.method1(“hello”,“world”)//使用字符串调用method1
}
def main(){
x=x()//构造函数
f(x)
}
  • 生成我提供的OO伪代码的“等效”Haskell代码的最佳方法是什么?我想:

    • 能够将带有方法的不可变类(可以有默认参数)转换为Haskell的代码。(保留惰性,因此我不想使用丑陋的
      IORefs
      和模拟可变数据结构)
    • 强制用户显式编写任何类型,因此我可以使用所有可用的Haskell机制来允许自动类型推断,比如使用为给定方法自动生成typeclass实例(等等)
    • 能够用我的编译器生成这样的代码,而不需要实现我自己的类型推断器(如果没有其他解决方案,也可以用我自己的类型推断器)
    • 生成快速二进制文件的结果代码(在编译时可以很好地优化)
  • 如果下面建议的工作流是最好的,我们如何修复建议的Haskell代码,使
    f con_X
    f con_Y
    都能工作?(见下文)

  • 当前工作状态

    伪代码可以轻松转换为以下Haskell代码(它是手写的,不是生成的,更易于阅读):

    上述代码不起作用,因为Haskell无法推断大于1的隐式类型,如
    f
    的类型。在对#haskell irc进行了一些讨论后,找到了一个部分解决方案,即我们可以翻译以下伪代码:

    X类{
    def方法1(a、b){
    (a,b)//返回
    }
    }
    Y类{
    def方法1(a、b){
    返回
    }
    }
    def f(x){
    印刷(x.1(1,2))
    打印(x.method1(“你好”,“世界”))
    }
    def main(){
    x=x()
    y=y()
    f(x)
    f(y)
    }
    
    要查看Haskell代码:

    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE FunctionalDependencies #-}
    {-# LANGUAGE RankNTypes #-}
    {-# LANGUAGE FlexibleContexts #-}
    
    
    data Y a = Y { _methody1 :: a } deriving(Show)
    data X a = X { _methodx1 :: a } deriving(Show)
    
    con_X = X { _methodx1 = (\a b -> (a,b)) }
    con_Y = Y { _methody1 = (\a b -> a) }
    
    class F_method1 cls sig where
      method1 :: cls sig -> sig
    
    instance F_method1 X a where
      method1 = _methodx1
    
    instance F_method1 Y a where
      method1 = _methody1
    
    f :: (F_method1 m (Int -> Int -> (Int, Int)),
          F_method1 m (String -> String -> (String, String)))
          => (forall a. (Show a, F_method1 m (a -> a -> (a,a))) => m (a -> a -> (a, a))) -> IO ()
    f x = do
      print $ (method1 x) (1::Int) (2::Int)
      print $ (method1 x) ("Hello ") ("World")
    
    main = do
      f con_X
      -- f con_Y
    
    此代码确实有效,但仅适用于数据类型
    X
    (因为它在
    f
    的签名中硬编码了
    method1
    的返回类型)。行
    f con_Y
    不起作用。 此外,是否有任何方法可以自动生成
    f
    的签名,或者我必须为此编写自己的类型推断器

    更新

    Crazy FIZRUK提供的解决方案确实适用于这种特定情况,但是使用了
    存在数据类型
    ,比如
    data Printable=forall a.Show a=>Printable a
    强制所有具有特定名称(即“method1”)的方法在所有可能的类中具有相同的结果类型,这不是我想要实现的

    下面的例子清楚地说明了我的意思:

    (伪代码):

    X类{
    def方法1(a、b){
    (a,b)//返回
    }
    }
    Y类{
    def方法1(a、b){
    返回
    }
    }
    def f(x){
    印刷(x.1(1,2))
    x、 方法1(“你好”,“世界”)//返回
    }
    def main(){
    x=x()
    y=y()
    print(f(x).fst())//fst返回第一个元组元素集,并且未为字符串定义
    print(f(y).length())//length返回字符串的长度,未为元组定义
    }
    
    是否可以将此类代码转换为Haskell,从而允许
    f
    根据其参数类型返回特定类型的结果?

    解决方案 好的,这就是模拟所需行为的方法。您将需要两个扩展,即
    RankNTypes
    存在量化

    首先,将rank-2类型放入
    X
    Y
    ,因为它是类方法的属性(我这里指的是OO类):

    接下来,您需要指定返回类型为“method”的属性。这是因为在
    f
    中调用
    method
    时,您不知道正在使用的类的实现。您可以使用typeclass约束返回类型,也可以使用(我不确定最后一个)。我将演示第一个变体

    我将约束包装为存在类型
    Printable

    data Printable = forall a. Show a => Printable a
    
    instance Show Printable where
        show (Printable x) = show x
    
    现在,我们可以定义我们将在
    f
    的类型签名中使用的所需接口:

    class MyInterface c where
        method :: forall a b. (Show a, Show b) => (a, b) -> c -> Printable
    
    接口也是多态的,这一点很重要。我将参数放在元组中,以模仿常见的OOP语法(见下文)

    X
    Y
    的实例很简单:

    instance MyInterface X where
        method args x = Printable . uncurry (_X'method x) $ args
    
    instance MyInterface Y where
        method args y = Printable . uncurry (_Y'method y) $ args
    
    现在可以简单地编写
    f

    f :: MyInterface c => c -> IO ()
    f obj = do
        print $ obj & method(1, 2)
        print $ obj & method("Hello, ", "there")
    
    现在我们可以创建一些OO类的对象
    X
    Y

    objX :: X
    objX = X $ λa b -> (a, b)
    
    objY :: Y
    objY = Y $ λa b -> a
    
    然后跑

    main :: IO ()
    main = do
        f objX
        f objY
    
    利润


    方便语法的助手函数:

    (&) :: a -> (a -> b) -> b
    x & f = f x
    

    我相信您必须自己对方法的类型进行一些类型推断。否则,只需修改名称以提供名称空间,并使用独立函数作为隐藏的方法。传入字段记录。这样做将使haskell有更好的机会进行类型推断这听起来很愚蠢,但是。。。您可以为每个方法调用传递一个单独的
    X
    记录吗?例如,编译
    f
    to
    fx1x2=print(method1x1(1::Int)(2::Int))>>print(method1x2“Hello”“World”)
    并编译
    main
    main=f co
    
    main :: IO ()
    main = do
        f objX
        f objY
    
    (&) :: a -> (a -> b) -> b
    x & f = f x