[英]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.