将类型为“t”的运算符转换为类型为“a-> t”的运算符

时间:2015-04-20 21:12:02

标签: haskell

这是将类型t上的运算符转换为类型a->t上的运算符的标准方法吗?即,这是一个实现此功能的lib(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

感谢我的&#34;函数转换器&#34;,我可以轻松管理有界值:

($&&) = fT (&&)
boundedIntField = checkBool ((>= 0) $&& (<= 100)) "error: this entry has to be between 0 and 100" intField

2 个答案:

答案 0 :(得分:6)

您真正需要的是liftA2liftM2组合子(分别来自Control.ApplicativeControl.Monad)。 liftM2liftA2的工作方式相同,因为所有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

如果我们用set notation([0, 100] U [200, 400] U [1000, inf)设置了union)写这个数字,那么检查该数字是否是U的元素。

答案 1 :(得分:2)

此代码位于Control.Applicative模块中:

instance Applicative ((->) r) where
    pure a = \_ -> a
    ff <*> fa = \r -> ff r (fa r)

liftA2 :: Applicative f => f (a -> b -> c) -> f a -> f b -> f c
liftA2 f fa fb = pure f <*> fa <*> fb

我们fT功能正是我们采用liftA2并使用(->) a代表ft代表a时所获得的功能, bc。我们可以像这样重写你的函数:

fT :: (t -> t -> t) -> (a -> t) -> (a -> t) -> (a -> t)
fT = liftA2

通过替换,我们可以证明它与您的相同:

liftA2 op f1 f2
    = (pure op <*> f1) <*> f2
    = \a -> (pure op <*> f1) a (f2 a)
    = \a -> (\a -> pure op a (fa 1)) a (f2 a)
    = \a -> (\a -> (\_ -> op) a (f1 a)) a (f2 a)
    = \a -> (\a -> op (f1 a)) a (f2 a)
    = \a -> op (f1 a) (f2 a)
    = \a -> (f1 a) `op` (f2 a)