簡體   English   中英

Endofunction為Monoid

[英]Endofunction as Monoid

我正在嘗試這個(用於學習目的):

{-# LANGUAGE FlexibleInstances #-}

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

期望id <> id等於id . id id . id

但是,使用(id <> id) 1我收到此錯誤:

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

我應該改變什么來運行它?

這只是為了更好地理解monoids和Haskell類型類,而不是用於任何實際用途

這將需要{-# OVERLAPPING #-} pragma,因為當b是Monoid時,GHC.Base有一個Monoid (a -> b)實例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 ,即使a是一個Monoid:

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

而使用Monoid b => a -> b ,將調用GHC.Base中的實例:

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

請注意, Data.Monoid提供與您的完全相同的實例a -> a但是使用newtype Endo a繞過重疊。

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

但另一個數學觀點是,它們只是一個對象的類別。 如果我們有一個幺半群,我們可以很容易地把它變成一個類別:

-- 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實例讓我感到難過。 我認為使用Cat'方法將Monoid實例用於基於其Category實例的函數會更自然

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)

為了某些目的,也許這是Data.Monoid.Endo的方法,但由於我們已經使用了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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM