函数类型作为幺半群实例

3
我有一个类似于这样的函数:
transition :: State -> ([State], [State])

鉴于我的问题领域,我知道如何链接两个连续的transition函数调用,就像这样:

transition `chain` trainsition ... `chain` transition

然而,我希望将其表达为Monoid并使用<>mappend进行链接。不幸的是,我似乎不能使以下内容或类似变体起作用:

instance Monoid (State -> ([State], [State])) where
    mempty  = ...
    mappend = ...

返回的错误如下:
Illegal instance declaration for
    ‘Monoid (State -> ([State], [State]))’
    (All instance types must be of the form (T a1 ... an)
     where a1 ... an are *distinct type variables*,
     and each type variable appears at most once in the instance head.
     Use FlexibleInstances if you want to disable this.)
• In the instance declaration for
    ‘Monoid (State -> ([State], [State]))’

一般来说,如何将函数表示为Monoid的实例?


如果您按照错误信息中给出的建议去做会发生什么? - jakubdaniel
我猜这意味着你需要一个包装器来封装这个函数,可以是数据构造器或者新类型。然而,即使如此,像这样的Monoids也存在:https://dev59.com/WKLia4cB1Zd3GeqPg0oW#44344821。他们是如何在实例中使用函数类型的? - Anton Xue
它们是参数化的,如果你开始混合不同类型的混凝土,你要么需要包装你的类型,要么尝试允许FlexibleInstances扩展。 - jakubdaniel
正如你所链接的问题所示,只要返回类型是Monoid,箭头类型就有一个实例Monoid。如果ab都是Monoid,那么(a, b)也是Monoid,因此Haskell可能会选择与您认为的不同的实例。 - jakubdaniel
谢谢你的帮助!经过一些调整,我成功地制作了一个Monad版本。 - Anton Xue
显示剩余2条评论
1个回答

7

在不同的方式中,函数已经是单子类型的实例了。您希望Haskell如何决定使用哪个实例来解决您的问题呢?解决您问题的常规方法是声明一个newtype包装器,例如:

newtype Transition a = Transition { runTransition :: a -> ([a], [a]) }

然后,您可以很好地创建您的单子类型实例:
instance Monoid (Transition a) where
  mempty  = ...
  mappend = ...

完成这个后,你甚至可以发现foldMap很有用。不必像下面这样写:

runTransition (Transition  transition `chain`
               Transition  transition `chain`
               ...
               Transition  transition)

您可以使用 foldMap 函数。
runTransition (foldMap Transition [transition, transition, ... transition])

我现在回想起来,经过尝试了几种方法后,突然有了很多感悟:你只能在“包装”对象(如newtype)上使用mappend,这意味着你不能随意执行:transition \mappend` transition。这些将被委托给预加载的Monoid实现之一。相反,你必须像你写的那样做:(TransitionT transition) `mappend` (TransitionT transition)`。谢谢! - Anton Xue

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接