这是将类型 t
上的运算符转换为类型 a->t
上的运算符的标准方法吗?即,这是一个实现此功能的库吗(fT
像 functionTransformer
):
fT :: (t -> t -> t) -> (a -> t) -> (a -> t) -> (a -> t)
fT op f1 f2 = \x -> (f1 x) `op` (f2 x)
(我们可以推广到 fT::(t1 -> t2 -> t) -> (t3 -> t1) -> (t3 -> t2) -> t3 -> t
)
我在学习 Yesod 时问过这个问题:在这个框架中,我们可以通过 checkBool
为字段添加验证条件。例如,我可以创建一个只接受大于 100 的字段:
smallIntField = checkBool (<= 100) "error: this entry has to be smaller than 100" intField
感谢我的“函数转换器”,我可以通过以下方式轻松管理有界值:
($&&) = fT (&&)
boundedIntField = checkBool ((>= 0) $&& (<= 100)) "error: this entry has to be between 0 and 100" intField
最佳答案
您真正需要的是liftA2
或 liftM2
组合子(分别来自 Control.Applicative
或 Control.Monad
)。 liftM2
将与 liftA2
一样工作因为所有的单子(monad)都是应用程序,但是如果你想或多或少地限制,这取决于你。使用 monadic 实现也会强制参数的求值顺序,liftA2
无法保证。 .
你可以把它当作
(<&&>) :: Applicative f => f Bool -> f Bool -> f Bool
(<&&>) = liftA2 (&&)
对于像 (>= 0)
这样的函数或 (<= 100)
, Applicative f => f
专攻 (Ord a, Num a) => (->) a
,所以类型签名是
(<&&>) :: (Ord a, Num a) => (a -> Bool) -> (a -> Bool) -> (a -> Bool)
所以你可以写类似的东西
between :: Int -> Int -> Int -> Bool
between lower upper = (lower <=) <&&> (<= upper)
如果您定义了 <||> = liftM2 (||)
那么你甚至可以做一些更复杂的事情,比如
between 0 100 <||> between 200 400 <||> (>= 1000) :: Int -> Bool
这将检查数字是否是 [0, 100] U [200, 400] U [1000, inf)
的元素如果我们用集合表示法写这个(U
是集合并集)。
关于haskell - 将类型 `t` 上的运算符转换为类型 `a->t` 上的运算符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29758493/