dmz*_*rsk 6 haskell composition applicative
我对命令式编程有很好的把握,但现在我自学了一个Haskell非常好.
我想,我对Monads,Functors和Applicatives有很好的理论认识,但我需要一些练习.而对于练习,我有时会从当前的工作任务中带来一些东西.
而且我在应用方式上结合了一些东西
我有两个验证功能:
import Prelude hiding (even)
even :: Integer -> Maybe Integer
even x = if rem x 2 == 0 then Just x else Nothing
isSmall :: Integer -> Maybe Integer
isSmall x = if x < 10 then Just x else Nothing
Run Code Online (Sandbox Code Playgroud)
现在我想validate :: Integer -> Maybe Integer从even和建造isSmall
我最好的解决方案是
validate a = isSmall a *> even a *> Just a
Run Code Online (Sandbox Code Playgroud)
这不是免费的
我可以使用monad
validate x = do
even x
isSmall x
return x
Run Code Online (Sandbox Code Playgroud)
但是为什么要使用Monad,如果(我想)我需要的只是一个应用?(它仍然没有免费)
这样做是否更好(也更简单)?
现在我有两个具有不同签名的验证器:
even = ...
greater :: (Integer, Integer) -> Maybe (Integer, Integer)
-- tuple's second element should be greater than the first
greater (a, b) = if a >= b then Nothing else Just (a, b)
Run Code Online (Sandbox Code Playgroud)
我需要validate :: (Integer, Integer) -> Maybe (Integer, Integer),它尝试greater输入元组,然后even是元组的第二个元素.
并且validate' :: (Integer, Integer) -> Maybe Integer具有相同的逻辑,但返回元组的第二个元素.
validate (a, b) = greater (a, b) *> even b *> Just (a, b)
validate' (a, b) = greater (a, b) *> even b *> Just b
Run Code Online (Sandbox Code Playgroud)
但我想的是,元组输入"流"入greater,然后"流"到某种的组合物snd和even然后仅单个元件在最终结束Just.
哈斯克勒会做什么?
当您编写表单的验证器时,a -> Maybe b您对整个类型比对Maybe应用程序更感兴趣.类型a -> Maybe b是Maybemonad 的Kleisli箭头.您可以制作一些工具来帮助使用此类型.
对于您可以定义的第一个问题
(>*>) :: Applicative f => (t -> f a) -> (t -> f b) -> t -> f b
(f >*> g) x = f x *> g x
infixr 3 >*>
Run Code Online (Sandbox Code Playgroud)
和写
validate = isSmall >*> even
Run Code Online (Sandbox Code Playgroud)
你的第二个例子是
validate = even . snd >*> greater
validate' = even . snd >*> fmap snd . greater
Run Code Online (Sandbox Code Playgroud)
它们以不同的顺序检查条件.如果您关心评估顺序,则可以定义另一个功能<*<.
如果您a -> Maybe b经常使用该类型,则可能需要为其创建一个类型newtype,以便您可以根据需要添加自己的实例.在newtype已经存在; 它是ReaderT,它的实例已经做你想做的事情.
newtype ReaderT r m a = ReaderT { runReaderT :: r -> m a }
Run Code Online (Sandbox Code Playgroud)
当您使用类型r -> Maybe a作为验证器来验证和转换单个输入时,r它与...相同ReaderT r Maybe.通过将两个函数应用于同一输入,然后将它们组合在一起,将它们中的两个组合在一起 的Applicative实例ReaderT<*>:
instance (Applicative m) => Applicative (ReaderT r m) where
f <*> v = ReaderT $ \ r -> runReaderT f r <*> runReaderT v r
...
Run Code Online (Sandbox Code Playgroud)
ReaderT的<*>几乎完全一样>*>从第一部分,但它并没有丢弃的第一个结果.ReaderT的*>是完全一样的>*>从所述第一部分.
就ReaderT你的例子而言
import Control.Monad.Trans.ReaderT
checkEven :: ReaderT Integer Maybe Integer
checkEven = ReaderT $ \x -> if rem x 2 == 0 then Just x else Nothing
checkSmall = ReaderT Integer Maybe Integer
checkSmall = ReaderT $ \x -> if x < 10 then Just x else Nothing
validate = checkSmall *> checkEven
Run Code Online (Sandbox Code Playgroud)
和
checkGreater = ReaderT (Integer, Integer) Maybe (Integer, Integer)
checkGreater = ReaderT $ \(a, b) = if a >= b then Nothing else Just (a, b)
validate = checkGreater <* withReaderT snd checkEven
validate' = snd <$> validate
Run Code Online (Sandbox Code Playgroud)
您可以使用这些中的一个ReaderT验证器上的值x由runReaderT validate x
你问为什么使用Monad,如果你需要的只是申请人?我可以问 - 为什么使用Applicative,如果你需要的只是Monoid?
你所做的一切主要是试图利用monoid行为/ Monoid,但试图通过Applicative接口来实现.有点像有工作Int,通过自己的字符串表示的(实现+了串"1"并"12",并串,而不只是工作1和12与工作整数)
请注意,您可以Applicative从任何Monoid实例获取实例,因此找到可以解决问题的Monoid与找到可以实现的Applicative相同.
even :: Integer -> All
even x = All (rem x 2 == 0)
isSmall :: Integer -> All
isSmall x = All (x < 10)
greater :: (Integer, Integer) -> All
greater (a, b) = All (b > a)
Run Code Online (Sandbox Code Playgroud)
为了证明它们是相同的,我们可以来回编写转换函数:
convertToMaybeFunc :: (a -> All) -> (a -> Maybe a)
convertToMaybeFunc f x = guard (getAll (f x)) $> x
-- assuming the resulting Just contains no new information
convertFromMaybeFunc :: (a -> Maybe b) -> (a -> All)
convertFromMaybeFunc f x = maybe (All False) (\_ -> All True) (f x)
Run Code Online (Sandbox Code Playgroud)
你可以直接写你的validate:
validate :: Int -> All
validate a = isSmall a <> even a
Run Code Online (Sandbox Code Playgroud)
但你也可以用你想要的提升风格来写它:
validate :: Int -> All
validate = isSmall <> even
Run Code Online (Sandbox Code Playgroud)
想做记号吗?
validate :: Int -> All
validate = execWriter $ do
tell isSmall
tell even
tell (other validator)
validate' :: (Int, Int) -> All
validate' = execWriter $ do
tell (isSmall . fst)
tell (isSmall . snd)
tell greater
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,每个Monoid实例都会产生Applicative/ Monadinstance(通过Writer和tell),这使得这有点方便.您可以将Writer/ tell在这里视为"提升" Monoid实例到free Applicative/ Monadinstance.
最后,你注意到一个有用的设计模式/抽象,但它确实是你注意到的幺半群.你已经注意通过Applicative接口处理那个monoid,不知何故......但是直接使用monoid可能更简单.
也,
validate :: Int -> All
validate = mconcat
[ isSmall
, even
, other validator
]
Run Code Online (Sandbox Code Playgroud)
可以说与写作版本的符号版本清晰可比:)
| 归档时间: |
|
| 查看次数: |
167 次 |
| 最近记录: |