我已经在我的两种数据类型中发现了一些常见的功能,所以就像任何值得他的盐的程序员一样,我试图将其分解出来:
module Binary where
import Control.Applicative
import Data.Function
import Control.Monad
class Binary f where
yes :: f a a
no :: f a b
(..>) :: f a b -> f b c -> f a c
yes' :: f a ()
(~.>) :: f a b -> f a c -> f a c
try :: (Binary f, Alternative (f a)) => f a a -> f a a
try = (<|> yes)
try' :: (Binary f, Alternative (f a)) => f a () -> f a ()
try' = (<|> yes')
(.>) :: (Binary f, Alternative (f c)) => f a c -> f c c -> f a c
a .> b = a ..> try b
(~>) :: (Binary f, Alternative (f a)) => f a b -> f a () -> f a ()
a ~> b = a ~.> try' b
greedy :: (Binary f, Alternative (f a)) => f a a -> f a a
greedy = fix $ ap (.>)
greedy' :: (Binary f, Alternative (f a)) => f a () -> f a ()
greedy' = fix $ ap (~>)
正如您所看到的,yes
和yes'
以及..>
和~.>
的类型略有不同 - 它们需要我来编写实例 - 以及所以我最终得到了重复的功能。
有没有办法可以摆脱yes'
和~.>
,仍然可以使用这些类型创建二进制实例?
以下是我的两个实例:
module Example where
import Binary
import Prelude hiding ((.), id)
import Control.Category
import Data.List.Zipper as Z
import Control.Monad.Trans.Maybe
import Control.Monad.State
newtype Opt a b = Opt { runOpt :: a -> Maybe b }
instance Category Opt where
id = yes
(Opt f) . (Opt g) = Opt $ g >=> f
instance Binary Opt where
yes = Opt Just
no = Opt $ const Nothing
(..>) = (>>>)
---------
type Tape = Zipper
newtype Machine a b = Machine { unMachine :: MaybeT (State (Tape a)) b }
instance Functor (Machine a) where
fmap f (Machine x) = Machine $ f <$> x
instance Applicative (Machine a) where
pure = Machine . pure
(Machine f) <*> (Machine x) = Machine $ f <*> x
instance Monad (Machine a) where
(Machine a) >>= f = Machine $ a >>= unMachine <$> f
instance Binary Machine where
no = Machine mzero
yes' = pure ()
a ~.> b = a >> b
答案 0 :(得分:2)
我认为你的两个实例存在微妙的不一致 - 也就是说,Opt
和Machine
没有足够的共同点来分享这么多的结构。例如,方法
yes :: f a a
(..>) :: f a b -> f b c -> f a c
基本上是Category
,正如您所注意到的那样(尽管我只是将Category
设为Binary
的超类,而不是重复这些方法)。但是Machine
不是一个类别,因为它不支持组合。此外,Opt
是一个profunctor(第一个参数中的逆变量,第二个参数中的协变量),而Machine
在其第一个参数上是不变的。这些是我的提示,在尝试抽象这些类型之前需要更改某些内容。
我怀疑Machine
缺少参数,而state参数实际上是Binary
抽象的外部参数。尝试使用你的monad的Kleisli category。
newtype Machine s a b = Machine { unMachine :: a -> MaybeT (State (Tape s)) b }
现在Machine s
是Category
,与Binary
的{{1}}相同,而且您不需要任何已启动的组合器,并且您可以表达如果需要,可以将Opt
作为Machine a b
,但您也可以概括它们。
事实上,您正在寻找的抽象可能只是ArrowZero
。 Machine a () b
的结构比Arrow
多一点,因此您应该考虑Category
的其余部分是否适用于您的问题。如果是这样,你刚刚开了一个新的组合器工具箱,你不需要手工编写任何实例,因为它们都包含在:
Arrow
(或 type Opt = Kleisli Maybe
type Machine s = Kleisli (MaybeT (State s))
样式newtype
如果您愿意的话)