不同类型的通用功能

时间:2018-02-10 22:53:41

标签: haskell

我已经在我的两种数据类型中发现了一些常见的功能,所以就像任何值得他的盐的程序员一样,我试图将其分解出来:

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 (~>)

正如您所看到的,yesyes'以及..>~.>的类型略有不同 - 它们需要我来编写实例 - 以及所以我最终得到了重复的功能。

有没有办法可以摆脱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

1 个答案:

答案 0 :(得分:2)

我认为你的两个实例存在微妙的不一致 - 也就是说,OptMachine没有足够的共同点来分享这么多的结构。例如,方法

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 sCategory,与Binary的{​​{1}}相同,而且您不需要任何已启动的组合器,并且您可以表达如果需要,可以将Opt作为Machine a b,但您也可以概括它们。

事实上,您正在寻找的抽象可能只是ArrowZeroMachine a () b的结构比Arrow多一点,因此您应该考虑Category的其余部分是否适用于您的问题。如果是这样,你刚刚开了一个新的组合器工具箱,你不需要手工编写任何实例,因为它们都包含在:

Arrow

(或 type Opt = Kleisli Maybe type Machine s = Kleisli (MaybeT (State s)) 样式newtype如果您愿意的话)