概括可能和haskell中的任何一个函数

时间:2014-05-09 13:28:45

标签: generics haskell

haskell中是否有一个函数可以推广可能和任何一个函数?

例如,我想象的是这样的函数:

generalizedFunc :: SOMETHING m => b -> (a -> b) -> m a -> b
generalizedFunc valForError f value = ...

在ghci中使用它看起来像这样:

> generalizedFunc "bye" (\_ -> "hello") (Just 3)
"hello"
> generalizedFunc "bye" (\_ -> "hello") Nothing
"bye"
> generalizedFunc "bye" (\_ -> "hello") (Right 3)
"hello"
> generalizedFunc "bye" (\_ -> "hello") (Left "error")
"bye"

注意:Tom Ellis明确表示这不是Either的概括,而是专业化。

2 个答案:

答案 0 :(得分:5)

是。您正在寻找的是Data.Foldable。它将foldr(对于列表)概括为任何代数数据类型:

Data.List.foldr     :: (a -> b -> b) -> b     ->   []       a -> b
maybe               :: b -> (a -> b)          ->   Maybe    a -> b
either              :: (a -> c) -> (b -> c)   ->   Either a b -> c
---
Data.Foldable.foldr :: Foldable t
                    => (a -> b -> b) -> b     ->   t        a -> b

您的代码会从generalizedFunc "bye" (\_ -> "hello")更改为foldr (\_ _ -> "hello") "bye"。确保告诉编译器你的意思是来自foldr的{​​{1}};请参阅模块的文档。

不幸的是,您的GHC版本可能缺少Data.Foldable个实例,但是自己编写一个应该相对容易。

答案 1 :(得分:3)

我想这是一些自我推销,但我会发布它,因为它非常相关。

我写了一个名为generic-church的小型图书馆。这将导出具有两个函数的单个类型类。要使用它,只需执行类似

的操作
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Data.Church

data Nat = Z | S Nat

instance ChurchRep Nat

请注意,Nat不一定是Foldable的仿函数。

然后我们可以启动GHCi并执行类似

的操作
*> (toChurch Z) 'a' (const 'b')
  'a'
*> (toChurch $ S Z) 'a' (const 'b')
  'b'

foldr不同,您可以自动将教堂表示转换回具体的数据类型

*> fromChurch (\a b -> a) :: Nat
  Z
*> fromChurch (a b -> b Z) :: Nat
  S Z

与catamorphisms / folds相比,现在有一个明显的缺点是generic-church目前以一种稍微天真的方式处理递归数据类型(尽管我正在寻找现在很快修复它)。也就是说,它将任何递归类型视为在其教会表示中展开一个级别,因此Nat的教会表示是

type Nat' = forall c. c -> (Nat -> c) -> c

编辑,您的generalFunc

以下是以实际推广generalFunc的方式编写maybe的代码。我需要一个函数toChurchP,它在我输入之前大约5分钟上传到generic-church-0.2。如果要运行此代码,请务必使用cabal update:)

{-# LANGUAGE DeriveGeneric, MultiParamTypeClasses, FlexibleInstances, TypeFamilies, ScopedTypeVariables #-}
module So where
import Data.Church
import GHC.Generics
import Data.Proxy

-- Generalize of const. Allows us to use more than just Either
-- for gmaybe
class GConst a r where
  gconst :: r -> a
instance GConst r' r => GConst (a -> r') r where
  gconst r = const (gconst r)
instance GConst r r where
  gconst = id

gfail :: forall a e s e' r. 
          (ChurchRep a, GConst e' e, Church a r ~ (e' -> s -> r)) =>
           e -> s -> a -> r
gfail e s a = toChurchP (Proxy :: Proxy r) a (gconst e :: e') s

这取决于gconst吞下给失败结果的所有参数e。一个聪明的定义应该允许像

这样的东西
> gfail id (const 'a') $ Left 'b'
  'b'

但我目前的表述需要-XIncoherentInstances(eek!)。我希望更新一个实际上会推广eithermaybe的版本。

因为它仍然有点漂亮。

*So> gfail True id (Just False)
False
*So> gfail True (id :: Bool -> Bool) Nothing
True
*So> gfail 'a' (const 'b') (Left ())
'a'
*So> gfail 'a' (const 'b') (Right ())
'b'
*So> :set -XDeriveGeneric
*So> data MyMaybe = Fail | Yay Int deriving (Show, Generic)
*So> instance ChurchRep MyMaybe
*So> gfail 'a' (const 'b') (Yay 1)
'b'