是否可以在Haskell中使用可调用对象定义类型?

Dmi*_*kin 10 haskell

我对haskell很新,对它的类型系统还不太满意.我想知道,如果有能力定义类型(数据类型?),哪些实例可以作为函数调用?

类似物是

__call__ 
Run Code Online (Sandbox Code Playgroud)

Python中的方法或类方法

operator() 
Run Code Online (Sandbox Code Playgroud)

在c ++中.(更多示例在维基百科中给出"功能对象"一词).

应用这种结构的例子是Polynom.该对象由其系数列表定义,即我希望有这样的类型:

data Num a => Polynom a = Polynom [a]
                          deriving (...)
Run Code Online (Sandbox Code Playgroud)

现在我当然可以定义功能

callPoly :: Num a => (Polynom a) -> a -> a
callPoly p x = ... -- implementation: extract coefficients of p,
                   -- construct polynomial and call it on x
Run Code Online (Sandbox Code Playgroud)

(这里我不打扰,能够在Floats上用Int系数调用多项式......这只是技术细节)

现在我可以在我的polinomial上调用它(在交互式提示符中):

let myPoly = Polynomial [1 2 3]
let applicationResult = callPoly myPoly 3
Run Code Online (Sandbox Code Playgroud)

但这种方式并不太花哨.希望能够直接调用多项式作为

let applicationResult = myPoly 3
Run Code Online (Sandbox Code Playgroud)

所以问题是:可以定义这样的多项式类型,可以调用哪些对象(实例)(用作函数)?可能是这种模式可能以其他方式实现,不涉及"数据"?可能是一些玩功能类型或smth.其他?

当然,这个想法不仅可以应用于多项式.实际上,对于任何必须"具有类型"并具有"一些附加数据"的函数(在多项式的情况下 - 它是系数).

或者,如果这是不可能的,那么是否有一些特定的原因或者它不被支持?

PS:在我看来,直接方法(如上所述)是不可能的,因为要可调用myPoly必须是类型(Int - > Int).但是类型(Int - > Int)不能附加任何数据(ig多项式系数).但我想确保,我是对的.

mer*_*ict 7

熟悉C++"函数对象"概念是很好的,因为这是对Haskell关于使用普通旧函数可以做什么的想法的一个很好的介绍......具体来说,currying,部分应用和传递函数作为其他函数的参数.

在C++中,您的代码看起来像:

class Polynomial {
  int coefficients[];
public:
  Polynomial(int coefficients[]) { /* ... */ }
  int operator() (int value) { /* ... */ }
};

int coefficients[] = {1, 2, 3};
Polynomial(coefficients)(4); // note the syntax here!
Run Code Online (Sandbox Code Playgroud)

这从根本上表达了一个单一的纯函数:一个多项式求值器,它取一系列的系数和一个值.它可以很容易地在C++中表达为:

int evaluatePolynomial(int coefficients[], int value);

int coefficients[] = {1, 2, 3};
evaluatePolynomial(coefficients, 4);
Run Code Online (Sandbox Code Playgroud)

但这种形式不是令行禁止为以前的形式.关于咖喱形式的好处是你可以说:

Polynomial p = Polynomial(coefficients);
p(4);
p(5);
p(6);
Run Code Online (Sandbox Code Playgroud)

代替:

evaluatePolynomial(coefficients, 4);
evaluatePolynomial(coefficients, 5);
evaluatePolynomial(coefficients, 6);
Run Code Online (Sandbox Code Playgroud)

好的.所以我们把这个"函数对象"的东西想象成一个面向对象的编程概念 - 一个伪装成函数的对象 - 但现在让我们忘记对象.如果你在Haskell中看一下它,它只是一个函数,不需要任何用户定义的数据类型来表达:

polynomial :: Num a => [a] -> a -> a
Run Code Online (Sandbox Code Playgroud)

你可以"正常"调用它(evaluatePolynomial()如上所述),立即将它应用于两个参数:

polynomial [1, 2, 3] 4
Run Code Online (Sandbox Code Playgroud)

但由于Haskell函数是curry,你可以部分应用(与Polynomial函数对象一样):

do
  let p = polynomial [1, 2, 3]
  print (p 4)
  print (p 5)
  print (p 6)
Run Code Online (Sandbox Code Playgroud)

十分简单.现在,如果你想做一些更接近C++的东西,你有一个代表你的Polynomial函数对象的特定数据类型,你可以这样做......

newtype Polynomial a = P (a -> a)  -- function object
mkPolynomial :: Num a => [a] -> Polynomial a  -- constructor
Run Code Online (Sandbox Code Playgroud)

......但这种额外的复杂性并没有带来任何好处.你立即注意到没有什么特别的Polynomial,它只是包装一个常规函数,所以你最终只需要再打开它,如:

do
  let (P p) = mkPolynomial [1, 2, 3]
  print (p 4)
  print (p 5)
  print (p 6)
Run Code Online (Sandbox Code Playgroud)

简而言之,你在思考问题的过程中,只是在功能而不是对象方面,你的Haskell代码最终会变得更简单,更惯用.


Ada*_*ner 4

我可能会弄错我的术语,因为我自己也不是 Haskell 的老手。然而,根据我对 Haskell 的理解:

由于 Haskell 不是面向对象的,因此它没有对象或实例(即传统意义上的)。您拥有数据类型的值,而不是数据类型的实例。话虽这么说,由于函数是数据(值),就像整数和字符串一样,因此您可以拥有可调用的值,因为它们可以携带自己的上下文(就像 OO 世界中的实例一样)。

如果您的目标是传递一个带有 的值PolyNom a,您可以简单地部分评估您的函数callPoly,然后将其视为您的“可调用多项式”。例子:

myPoly = PolyNom [1, 2, 3]
callMyPoly = callPoly myPoly

-- or simply

callMyPoly = callPoly (PolyNom [1, 2, 3])
Run Code Online (Sandbox Code Playgroud)

现在,callMyPoly 的类型是:

callMyPoly :: Num a => a -> a
Run Code Online (Sandbox Code Playgroud)

你可以这样称呼它:

callMyPoly 5
Run Code Online (Sandbox Code Playgroud)

这相当于:

callPoly myPoly 5
Run Code Online (Sandbox Code Playgroud)