oop – 创建绑定到Haskell中的记录的方法

我正在创建一个懒惰功能的DSL,它允许用户使用方法定义非可变结构(类似于OO语言的类,但不可变).我将该语言的代码编译成Haskell代码.

最近我遇到了这个工作流的问题.我不想强制用户写明确的类型,所以我想大量使用Haskell的类型参考.当我翻译一个函数时,会发生这个问题,该函数多次调用“对象”的多态方法,每次传递不同的参数类型,如下所示:

(伪):

class X {
   def method1(a, b) {
       (a, b) // return
   }
}
def f(x) {
   print (x.method1(1,2))              // call method1 using Ints
   print (x.method1("hello", "world")) // call method1 using Strings
}

def main() {
   x = X() // constructor
   f(x)
}

>我提供的OO伪代码的“等效”Haskell代码的最佳方法是什么?我想要:

>可以使用方法(可以具有默认参数)将非可变类转换为Haskell的代码. (保存懒惰,所以我不想使用丑陋的IORefs和模仿可变数据结构)
>不要强制用户显式写任何类型,所以我可以使用所有可用的Haskell机制来允许自动类型推断 – 就像使用Template Haskell自动生成给定方法的类型实例一样.
>能够使用我的编译器生成这样的代码,而不需要实现我自己的类型参与者(或者如果没有其他解决方案,或者我自己的类型参与者)
>结果代码来生成快速二进制文件(在编译时可以很好地优化).

>如果下面提出的工作流是最好的,那么我们如何解决所提出的Haskell代码,这样一来,f con_X和f con_Y都可以工作? (见下文)

当前工作状态

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

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

-- class and its constructor definition
data X a = X { _methodx1 :: a } deriving(Show)
con_X = X { _methodx1 = (\a b -> (a,b)) }

-- There can be other classes with "method1"
class F_method1 cls sig where
  method1 :: cls sig -> sig

instance F_method1 X a where
  method1 = _methodx1

f x = do
  print $(method1 x) (1::Int) (2::Int)
  print $(method1 x) ("Hello ") ("World")

main = do
  let x = con_X
  f x

上述代码不起作用,因为Haskell不能推断出高于1的rank的隐式类型,如f的类型.经过对#haskell irc的一些讨论,发现了一个部分解决方案,即我们可以翻译以下伪代码:

class X {
   def method1(a, b) {
       (a, b) // return
   }
}

class Y {
   def method1(a, b) {
       a // return
   }
}

def f(x) {
   print(x.method1(1, 2))
   print(x.method1("hello", "world"))
}

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___行不起作用.
另外,有没有办法自动生成f的签名,或者我必须为自己编写类型的参考文件?

UPDATE

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

以下示例清楚地显示了我的意思:

(伪):

class X {
   def method1(a, b) {
       (a, b) // return
   }
}

class Y {
   def method1(a, b) {
       a // return
   }
}

def f(x) {
   print(x.method1(1, 2))
   x.method1("hello", "world") // return
}

def main() {
   x = X()
   y = Y()
   print (f(x).fst())    // fst returns first tuple emenet and is not defined for string
   print (f(y).length()) // length returns length of String and is not defined for tuples
}

是否可以将这样的代码转换为Haskell,允许f根据其参数的类型返回特定类型的结果?

好的,这是你如何模仿所需的行为.您将需要两个扩展,即RankNTypes和ExistentialQuantification.

首先,将等级2类型放入X和Y.因为它是类方法的属性(我的意思是OO类在这里):

data X = X { _X'method :: forall a b. a -> b -> (a, b) }
data Y = Y { _Y'method :: forall a b. a -> b -> a }

接下来,您需要指定什么属性的返回类型为“method”.这是因为在f中调用方法时,您不知道您正在使用的类的实现.您可以使用类型类限制返回类型,也可以使用Data.Dynamic(我不确定最后一个).我将展示第一个变种.

我将约束存在一个存在类型可打印:

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
翻译自:https://stackoverflow.com/questions/19484232/creating-methods-bound-to-records-in-haskell

转载注明原文:oop – 创建绑定到Haskell中的记录的方法