簡體   English   中英

Haskell中泛型多態ADT的Functor實例?

[英]Functor instance for generic polymorphic ADTs in Haskell?

當將類別理論應用於泛型編程時,Haskell做得非常好,例如像recursion-schemes這樣的庫。 然而,我不確定的一件事是如何為多態類型創建通用仿函數實例。

如果你有一個多態類型,比如List或Tree,你可以創建一個從(Hask×Hask)到Hask的仿函數代表它們。 例如:

data ListF a b = NilF | ConsF a b  -- L(A,B) = 1+A×B
data TreeF a b = EmptyF | NodeF a b b -- T(A,B) = 1+A×B×B

這些類型在A上是多態的,但是關於B是固定點,如下所示:

newtype Fix f = Fix { unFix :: f (Fix f) }
type List a = Fix (ListF a)
type Tree a = Fix (TreeF a)

但正如大多數人知道,列表和樹木也都在通常意義上,他們代表的“容器”仿函數a的,這樣你可以映射函數f :: a -> b獲得的容器b的。

我試圖弄清楚是否有辦法以通用的方式將這些類型(固定點)作為Functor一個實例,但我不確定如何。 到目前為止我遇到了以下兩個問題:


1)首先,必須有一種方法來定義任何多態固定點上的通用gmap 知道像ListFTreeF這樣的類型是Bifunctors,到目前為止我已經得到了這個:

{-# LANGUAGE ScopedTypeVariables #-}
import Data.Bifunctor

newtype Fix f = Fix { unFix :: f (Fix f) }

cata :: Functor f => (f a -> a) -> Fix f -> a
cata f = f . fmap (cata f) . unFix

-- To explicitly use inF as the initial algebra
inF :: f (Fix f) -> Fix f
inF = Fix

gmap :: forall a b f. Bifunctor f => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
    where
        alg :: f a (Fix (f b)) -> Fix (f b)
        alg = inF . bimap f id

在Haskell中,這給了我以下錯誤: Could not deduce (Functor (fa)) arising from a use of cata from the context (Bifunctor f)

我正在使用bifunctors包,它有一個WrappedBifunctor類型,專門定義了以下可以解決上述問題的實例: Bifunctor p => Functor (WrappedBifunctor pa) 但是,我不確定如何在Fix “提升”這種類型以便能夠使用它

2)即使可以定義上面的通用gmap ,我也不知道是否可以創建一個具有fmap = gmapFunctor的通用實例,並且可以立即為那里的ListTree類型工作(以及以類似方式定義的任何其他類型)。 這可能嗎?

如果是這樣,是否可以使其與recursion-schemes兼容?

你可以說,如果你願意接受你正在與bifunctors打交道的那一刻

cata :: Bifunctor f => (f a r -> r) -> Fix (f a) -> r
cata f = f . bimap id (cata f) . unFix

然后

gmap :: forall a b f. Bifunctor f => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
    where
        alg :: f a (Fix (f b)) -> Fix (f b)
        alg = inF . bimap f id

(在gmap ,我剛剛重新安排了類約束,以使作用域類型變量起作用。)

您也可以使用原始版本的cata ,但是您需要在gmap上使用FunctorBifunctor約束:

gmap :: forall a b f. (Bifunctor f, Functor (f a)) => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = cata alg
    where
        alg :: f a (Fix (f b)) -> Fix (f b)
        alg = inF . bimap f id

你不能讓你的gmap成為普通Functor類的一個實例,因為它需要是類似的東西

instance ... => Functor (\ x -> Fix (f x))

而且我們沒有類型級別的lambda。 如果你反轉f的兩個參數,你可以這樣做,但是你失去了“其他” Functor實例,需要再次為Bifunctor定義cata

[您可能還有興趣閱讀http://www.andres-loeh.de/IndexedFunctors/以獲得更通用的方法。]

TBH我不確定這個解決方案對你有多大幫助,因為它仍然需要為這些定點newtype函數提供額外的新類型包裝,但是我們在這里:

如果你做一些包裝/展開,你可以繼續使用你的通用cata

給出以下兩個輔助函數:

unwrapFixBifunctor :: (Bifunctor f) => Fix (WrappedBifunctor f a) -> Fix (f a)
unwrapFixBifunctor = Fix . unwrapBifunctor . fmap unwrapFixBifunctor . unFix

wrapFixBifunctor :: (Bifunctor f) => Fix (f a) -> Fix (WrappedBifunctor f a)
wrapFixBifunctor = Fix . fmap wrapFixBifunctor . WrapBifunctor . unFix

你可以在f上定義gmap而不需要任何額外的約束:

gmap :: (Bifunctor f) => (a -> b) -> Fix (f a) -> Fix (f b)
gmap f = unwrapFixBifunctor . cata alg . wrapFixBifunctor
  where
    alg = inF . bimap f id

你可以Fix . f Fix . fFunctor經由newtype

我們可以實現一個Functor例如\\a -> Fix (fa)通過實施本“型級拉姆達”作為newtype

newtype FixF f a = FixF{ unFixF :: Fix (f a) }

instance (Bifunctor f) => Functor (FixF f) where
    fmap f = FixF . gmap f . unFixF

bifunctors包還提供了一個特別合適的Fix版本:

newtype Fix p a = In {out :: p (Fix p a) a}

這很容易成為一個Functor實例:

instance Bifunctor p => Functor (Fix p) where
  fmap f = In . bimap (fmap f) f . out

暫無
暫無

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

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