我有以下类型,并希望将其作为Functor:
newtype SubsM a = SubsM {runSubsM :: Context -> Either Error (a, Env)}
到目前为止我得到了这个
instance Functor SubsM where
fmap f (SubsM a) = SubsM (\s->(Right((f a),(fst s))))
我收到错误,因为a不是预期的类型,我的问题是我如何在左侧模式匹配a?
答案 0 :(得分:7)
当机器完全能够为你思考时,我宁愿不去思考。
{-# LANGUAGE DeriveFunctor #-}
newtype SubsM a = SubsM {runSubsM :: Context -> Either Error (a, Env)}
deriving Functor
答案 1 :(得分:5)
请注意,您还可以使用Functor
和->
的现有Either
个实例为您完成大部分工作:
instance Functor SubsM where
fmap g (SubsM cf) = SubsM (fmap (fmap (leftfmap g)) cf)
where
leftfmap f (a, b) = (f a, b)
Functor
(,)
实例映射在右侧值上,而不是左侧值,因此您无法使用fmap
。如果您从leftfmap
导入该功能,也可以将first
写为Control.Arrow
。
答案 2 :(得分:3)
您可以使Either Error (a, Env)
与case
进行模式匹配:
instance Functor SubsM where
fmap f (SubsM cf) = SubsM $ \c -> case (cf c) of
Left err -> Left err
Right (v, env) -> Right (f v, env)
在传播错误的Left
情况下,在Right
情况下,您解压缩结果对并将f
应用于第一个元素。
答案 3 :(得分:3)
由于Either a b
和(,)
都是Bifunctor
类的实例,因此已经完成了大部分必要的工作。
-- Using the tuple instance
first f (x, y) == (f x, y)
-- Using the Either instance
second g (Left err) = Left err -- basically, id
first g (Right v) = Right (g v)
使用这些功能,你可以大大缩短这个功能(从李的答案开始逐步减少):
import Data.Bifunctor
instance Functor SubsM where
fmap f = SubsM . second (first f) . runSubsM
有人真的会编写这样的代码,更不用说从头开始了吗?可能不是。它并不是很明显它是如何工作的,而是将其衍生出来 一步一步非常简单,您可能会发现其中一个中间步骤很有用。
我可能会写类似
的内容instance Functor SubsM where
fmap f (SubsM cf) = SubsM $ \c -> (second . first) f (cf c)
将更深奥的部分限制为单个函数(second . first)
。
首先,使用Bifunctor
的{{1}}实例来避免模式匹配
在元组上。
(,)
接下来,使用-- first f (v, env) == (f v, env)
instance Functor SubsM where
fmap f (SubsM cf) = SubsM $ \c -> case (cf c) of
Left err -> Left err
Right t -> Right (first f t)
Bifunctor
实例来避免Either a b
的返回值上的模式匹配:
cf c
您还可以通过解包来避免-- second (first f) (Left err) == Left Err
-- second (first f) (Right t) == Right (first f) t
instance Functor SubsM where
fmap f (SubsM cf) = SubsM $ \c -> second (first f) (cf c)
值上的模式匹配
它位于SubsM
的右侧:
runSubsM
最后,我们开始应用函数组合来消除明显 尽可能争论。
instance Functor SubsM where
fmap f cf = SubsM $ \c -> second (first f) ((runSubsM cf) c)