有没有办法在 haskell 中传递未知类型的运算符?
Is there a way to pass an operator with unknown type in haskell?
我有一个函数 f op = (op 1 2, op 1.0 2.0)
,它需要像这样工作:
f (+)
(3, 3.0)
但如果不声明 f
的类型,它的工作方式如下:
f (+)
(3.0, 3.0)
而且我正在努力声明 f
的类型。它应该采用一个运算符来处理 Num
的所有实例。在 Haskell 中甚至可能吗?
问题是您通过将运算符应用于 1.0
和 2.0
等小数来强制运算符处理 Fractional
类型。您的代码会进行类型检查,因为 Fractional
是 Num
的子类型(这意味着 Fractional
的每个实例也是 Num
的一个实例)。
GHCi 中的以下实验应该清楚:
Prelude> :t 0
0 :: Num p => p
Prelude> :t 0.0
0.0 :: Fractional p => p
Prelude> :t 0 + 0.0 -- Fractional taking advantage!
0 + 0.0 :: Fractional a => a
所以,如果你想让它在 Num
s 上完全工作,你只需要摆脱那些“.0”s:
Prelude> f op = (op 1 2, op 1 2)
Prelude> f (+)
(3, 3)
但是
如果您真的需要返回元组的第二个元素是 Fractional
而第一个元素是更一般的 Num
的行为,事情会变得有点复杂。
您传递给 f
的运算符(或者实际上是函数)必须同时出现 两种不同的类型 。这在普通 Haskell 中通常是不可能的,因为每个类型变量在每个应用程序中都有一个固定的赋值——这意味着 (+)
需要决定它是 Float
还是 Int
或者什么。
但是,这可以更改。您需要做的是通过在 GHCi 中写入 :set -XRank2Types
或在 .hs
文件的最顶部添加 {-# LANGUAGE Rank2Types #-}
来打开 Rank2Types
扩展。这将允许您以其参数类型更动态的方式编写 f
:
f :: (Num t1, Fractional t2)
=> (forall a. Num a => a -> a -> a) -> (t1, t2)
f op = (op 1 2, op 1.0 2.0)
现在,类型检查器不会将任何固定类型分配给 op
,而是将其保留为多态以进一步专门化。因此,它可以应用于同一上下文中的 1 :: Int
和 1.0 :: Float
。
的确如此,
Prelude> f (+)
(3,3.0)
来自评论的提示:可以放宽类型约束以使函数更通用:
f :: (Num t1, Num t2)
=> (forall a. Num a => a -> a -> a) -> (t1, t2)
f op = (op 1 2, op 1 2)
它在所有情况下都可以正常工作,以前版本的 f
会做一些更多的事情,其中返回对的 snd
可以是例如 Int
:
Prelude> f (+)
(3,3)
Prelude> f (+) :: (Int, Float)
(3,3.0)
我有一个函数 f op = (op 1 2, op 1.0 2.0)
,它需要像这样工作:
f (+)
(3, 3.0)
但如果不声明 f
的类型,它的工作方式如下:
f (+)
(3.0, 3.0)
而且我正在努力声明 f
的类型。它应该采用一个运算符来处理 Num
的所有实例。在 Haskell 中甚至可能吗?
问题是您通过将运算符应用于 1.0
和 2.0
等小数来强制运算符处理 Fractional
类型。您的代码会进行类型检查,因为 Fractional
是 Num
的子类型(这意味着 Fractional
的每个实例也是 Num
的一个实例)。
GHCi 中的以下实验应该清楚:
Prelude> :t 0
0 :: Num p => p
Prelude> :t 0.0
0.0 :: Fractional p => p
Prelude> :t 0 + 0.0 -- Fractional taking advantage!
0 + 0.0 :: Fractional a => a
所以,如果你想让它在 Num
s 上完全工作,你只需要摆脱那些“.0”s:
Prelude> f op = (op 1 2, op 1 2)
Prelude> f (+)
(3, 3)
但是
如果您真的需要返回元组的第二个元素是 Fractional
而第一个元素是更一般的 Num
的行为,事情会变得有点复杂。
您传递给 f
的运算符(或者实际上是函数)必须同时出现 两种不同的类型 。这在普通 Haskell 中通常是不可能的,因为每个类型变量在每个应用程序中都有一个固定的赋值——这意味着 (+)
需要决定它是 Float
还是 Int
或者什么。
但是,这可以更改。您需要做的是通过在 GHCi 中写入 :set -XRank2Types
或在 .hs
文件的最顶部添加 {-# LANGUAGE Rank2Types #-}
来打开 Rank2Types
扩展。这将允许您以其参数类型更动态的方式编写 f
:
f :: (Num t1, Fractional t2)
=> (forall a. Num a => a -> a -> a) -> (t1, t2)
f op = (op 1 2, op 1.0 2.0)
现在,类型检查器不会将任何固定类型分配给 op
,而是将其保留为多态以进一步专门化。因此,它可以应用于同一上下文中的 1 :: Int
和 1.0 :: Float
。
的确如此,
Prelude> f (+)
(3,3.0)
来自评论的提示:可以放宽类型约束以使函数更通用:
f :: (Num t1, Num t2)
=> (forall a. Num a => a -> a -> a) -> (t1, t2)
f op = (op 1 2, op 1 2)
它在所有情况下都可以正常工作,以前版本的 f
会做一些更多的事情,其中返回对的 snd
可以是例如 Int
:
Prelude> f (+)
(3,3)
Prelude> f (+) :: (Int, Float)
(3,3.0)