Woj*_*ilo 12 oop polymorphism haskell types higher-rank-types
我正在创建一个懒惰的,功能性的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)
}
Run Code Online (Sandbox Code Playgroud)
生成我提供的OO伪代码的"等效"Haskell代码的最佳方法是什么?我想要:
IORefs和模仿可变数据结构)如果提出下面的工作流程可能是最好的一个,我们如何解决所提出的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
Run Code Online (Sandbox Code Playgroud)
上面的代码不工作,因为Haskell中不能推断隐含的类型的秩大于1,类似的类型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)
}
Run Code Online (Sandbox Code Playgroud)
到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
Run Code Online (Sandbox Code Playgroud)
这段代码确实有效,但仅适用于数据类型X(因为它已经硬编码了method1签名中的返回类型f.该行f con_Y不起作用.另外,有没有办法自动生成签名f或者我必须编写自己的类型推论者呢?
UPDATE
由疯狂FIZRUK提供的解决方案确实适用于这种特殊情况下,但使用existential data types像data Printable = forall a. Show a => Printable a力具有特定名称(即"方法1")的所有方法具有相同的结果类型所有可能的类,这是我想达到不算什么.
以下示例清楚地显示了我的意思:
(伪):
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
}
Run Code Online (Sandbox Code Playgroud)
是否可以将此类代码转换为Haskell,允许f根据其参数的类型返回特定类型的结果?
好的,这就是您可以模仿所需行为的方法。您需要两个扩展,即RankNTypes和ExistentialQuantification。
首先,将rank-2类型放入Xand中Y中。因为它是类方法的属性(这里我指的是OO类):
data X = X { _X\'method :: forall a b. a -> b -> (a, b) }\ndata Y = Y { _Y\'method :: forall a b. a -> b -> a }\nRun Code Online (Sandbox Code Playgroud)\n\n接下来,您需要指定哪些属性的返回类型为“method”。这是因为在调用时method您f不知道您正在使用的类的实现。您可以使用类型类约束返回类型,或者可能使用Data.Dynamic(我不确定最后一个)。我将演示第一个变体。
我将把约束包装在存在类型中Printable:
data Printable = forall a. Show a => Printable a\n\ninstance Show Printable where\n show (Printable x) = show x\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们可以定义将在类型签名中使用的所需接口f:
class MyInterface c where\n method :: forall a b. (Show a, Show b) => (a, b) -> c -> Printable\nRun Code Online (Sandbox Code Playgroud)\n\n接口也是多态的,这一点很重要。我将参数放在一个元组中以模仿常见的 OOP 语法(见下文)。
\n\nX和 的实例Y很简单:
instance MyInterface X where\n method args x = Printable . uncurry (_X\'method x) $ args\n\ninstance MyInterface Y where\n method args y = Printable . uncurry (_Y\'method y) $ args\nRun Code Online (Sandbox Code Playgroud)\n\n现在f可以简单地写成:
f :: MyInterface c => c -> IO ()\nf obj = do\n print $ obj & method(1, 2)\n print $ obj & method("Hello, ", "there")\nRun Code Online (Sandbox Code Playgroud)\n\n现在我们可以创建一些 OO 类的对象X并且Y:
objX :: X\nobjX = X $ \xce\xbba b -> (a, b)\n\nobjY :: Y\nobjY = Y $ \xce\xbba b -> a\nRun Code Online (Sandbox Code Playgroud)\n\n并运行它!
\n\nmain :: IO ()\nmain = do\n f objX\n f objY\nRun Code Online (Sandbox Code Playgroud)\n\n利润!
\n\n方便语法的辅助函数:
\n\n(&) :: a -> (a -> b) -> b\nx & f = f x\nRun Code Online (Sandbox Code Playgroud)\n