在Haskell中,设置"对象"的类似模式是什么?与构造函数?

时间:2017-12-20 22:31:27

标签: haskell

在面向对象的编程语言中,您经常有多个构造函数来设置类的对象,例如,MyInteger对象可以从内置的int或字符串构造:

class MyInteger {
    int _value;

    MyInteger (int value) {
        _value = value;
    }

    MyInteger (String value) {
        _value = String.parseInt(value);
    }
}

在Haskell中执行此操作的惯用方法是什么?据我所知,在Haskell中总和类型

data MyInteger = Integer Int | String String

可以是IntegerString,也不是上述oo示例中的单一类型。我无法为值构造函数String定义一个逻辑,它将采用String和"返回" MyInteger

我是否必须定义这样的单独函数:

myIntegerFromString :: String -> MyInteger
myintegerFromString inputString = ...<parsing logic on inputString>....

然后每当我想从字符串

创建MyInteger时调用它
 main = do
     ...
     let aMyInteger = myIntegerFromString "4"
     ...

附录: 创建一个&#34;对象&#34;来自IntString仅仅是一个最小的例子。我对使用类型X的变量的一般方法更感兴趣,这些变量可以从各种其他类型Y,Z,...创建,以及如何在Haskell中解决这个问题。正是因为我不想在以面向对象的方式思考时滥用Haskell,所以我要求在Haskell中对这种模型进行建模。这就是为什么解决Int来自String s的解析的解决方案太具体了。

2 个答案:

答案 0 :(得分:2)

在您的示例中,MyInteger始终包含int,但有时通过首先将字符串解析为int来构造String。这不等同于data MyInteger = Integer Int | String String,而是等同于newtype MyInteger = MyInteger Intsmart constructor用于从字符串构建:

newtype MyInteger = MyInteger Int

mkMyIntegerStr :: String -> MyInteger
mkMyIntegerStr = MyInteger . read

需要注意的是,并非所有字符串都具有作为int的有效解析,并且在类型系统中确认此失败模式会更加健壮:

mkMyIntegerStr :: String -> Maybe MyInteger
mkMyIntegerStr = fmap MyInteger . readMaybe
来自Text.Read模块的readMaybe

总的来说,我认为最好是用新鲜的眼睛来接近Haskell,而不是试图将熟悉的OOP概念映射到它上面。试图将FP视为一种奇怪的OOP形式,导致许多学习者误入歧途。

答案 1 :(得分:1)

Haskell程序员使用'smart constructor'模式来执行此操作。需要说明的是,这不是设计选择,这是设计必需品。根本不可能完全按照你在Haskell中的OOP示例中所做的那样。

事实上,您可以在Data.Text模块中看到此样式。以下是Text类型的原始定义:

data Text = Text
    {-# UNPACK #-} !A.Array
    {-# UNPACK #-} !Int
    {-# UNPACK #-} !Int
    deriving (Typeable)

即使我是专家,我也不想触及它。但是,有一个很好的小智能构造函数:(虽然不可否认它不执行检查)

pack :: String -> Text
pack = unstream . S.map safe . S.streamList -- Scary internals handled cleanly.

但为什么我们没有内置这种面向对象的风格?因为构造函数的内部工作对您来说是隐藏的,所以当您编写new MyInteger("123")时,您并不确切知道可能会发生什么。这直接违反了Haskell的基本原则之一参考透明度

相反,Haskell程序员可能会将上述类翻译为:

newtype MyInteger = MyInteger Integer

parseMyInteger :: String -> MyInteger
parseMyInteger = -- (whatever implementation here)

总之,在编写Haskell时忘记面向对象的编程(除非你是being experimental)。 Haskell有一个完全不同的范例,所以使用它而不是OOP概念。

一些可选阅读:What is referential transparency?