Endofunction为Monoid

时间:2016-06-18 15:19:42

标签: haskell monoids

我尝试这个(出于学习目的):

{-# LANGUAGE FlexibleInstances #-}

instance Monoid (a -> a) where
  mempty = id
  mappend f g = f . g

期望id <> id等于id . id

但是,(id <> id) 1我收到此错误:

Non type-variable argument in the constraint: Monoid (a -> a)

我应该更改什么来运行它?

只是更好地理解monoids和Haskell类型类,而不是用于任何实际用途

2 个答案:

答案 0 :(得分:5)

这需要{-# OVERLAPPING #-} pragma,因为当b是Monoid时,GHC.Base有Monoid (a -> b)的实例:

{-# LANGUAGE FlexibleInstances #-}
import Data.Monoid (Monoid, mempty, mappend, (<>))

instance {-# OVERLAPPING #-} Monoid (a -> a) where
    mempty = id
    mappend f g = f . g

然后,即使a -> a是Monoid,也会为a调用上面的实例:

\> (id <> id) 1
1
\> (id <> id) [1]
[1]

而对于Monoid b => a -> b,将调用GHC.Base中的实例:

\> ((:[]) <> (:[])) 1
[1,1]

请注意,Data.Monoid提供an exact same instance as yours for a -> a,但使用newtype Endo a可以绕过重叠。

答案 1 :(得分:4)

Haskell Category类提供了处理类别的方法,这些类别的对象恰好是某种类型的Haskell类型。具体地,

class Category c where
  id :: c x x
  (.) :: c y z -> c x y -> c x z

方法的名称应该非常熟悉。值得注意的是,

instance Category (->) where
  id x = x
  f . g = \x -> f (g x)

你可能知道幺半群是具有身份的半群,用Haskell表示

class Monoid a where
  mappend :: a -> a -> a
  mempty :: a

但另一个数学观点是,他们只用一个对象来分类。如果我们有一个monoid,我们可以很容易地把它变成一个类别:

-- We don't really need this extension, but
-- invoking it will make the code below more useful.
{-# LANGUAGE PolyKinds #-}

import Control.Category
import Data.Monoid
import Prelude hiding ((.), id)

newtype Mon m a b = Mon m

instance Monoid m => Category (Mon m) where
  id = Mon mempty
  Mon x . Mon y = Mon (x `mappend` y)

走另一条路有点棘手。一种方法是选择一种只有一种类型的种类,并查看其唯一对象就是那种类型的类别(准备好令人讨厌的代码,如果你愿意,你可以跳过它;下面的位不那么可怕)。这表明我们可以在Category类型'()类型的()Monoid类型之间自由转换。该类别的箭头成为幺半群的元素。

{-# LANGUAGE DataKinds, GADTs, PolyKinds #-}

data Cat (c :: () -> () -> *) where
  Cat :: c '() '() -> Cat c
instance Category c => Monoid (Cat c) where
  mempty = Cat id
  Cat f `mappend` Cat g = Cat (f . g)

但这太可怕了! EW!如此紧密地固定下来并不能从实用的角度来完成任何事情。但是我们可以通过玩一个小技巧来获得功能而不会有太多混乱!

{-# LANGUAGE GADTs, PolyKinds #-} 

import Control.Category
import Data.Monoid
import Prelude hiding ((.), id)

newtype Cat' (c :: k -> k -> *) (a :: k) (b :: k) = Cat' (c a b)

instance (a ~ b, Category c) => Monoid (Cat' c a b) where
  mempty = Cat' id
  Cat' f `mappend` Cat' g = Cat' (f . g)

我们只是将自己局限于一次只查看一个对象,而不是将自己局限于只有一个对象的Category

现有的Monoid函数实例让我感到难过。我认为使用Monoid实例基于Category实例使用Cat'实例,使用instance a ~ b => Monoid (a -> b) where mempty = id mappend = (.) 方法会更加自然

Monoid

由于已经有newtype个实例,并且重叠的实例是邪恶的,我们必须使用newtype Morph a b = Morph {appMorph :: a -> b} 。我们可以使用

instance a ~ b => Monoid (Morph a b) where
  mempty = Morph id
  Morph f `mappend` Morph g = Morph (f . g)

然后写

newtype

出于某些目的,也许这是可行的方法,但由于我们已经使用了Data.Monoid.Endo,我们通常也可以删除非标准的相等上下文并使用newtype Endo a = Endo {appEndo :: a -> a} instance Monoid (Endo a) where mempty = Endo id Endo f `mappend` Endo g = Endo (f . g) ,它将相等性构建到类型中:

            $.ajax('URLtoLoad', {
                method:'GET'
            }).complete(function(res) {
                if(res.responseText) {
                    document.getElementById('myIframe').innerHTML = res.responseText;
                }
            });