这可能适用于任何类型类,但我们可以更好地了解Functors.我不想构建这个列表.
l = [Just 1, [1,2,3], Nothing, Right 4]
Run Code Online (Sandbox Code Playgroud)
然后
map (fmap (+1)) l
Run Code Online (Sandbox Code Playgroud)
要得到
[Just 2, [2,3,4], Nothing, Right 5]
Run Code Online (Sandbox Code Playgroud)
我知道它们都是含有Ints的Functors,所以它可能是有可能的.我怎样才能做到这一点?
编辑
事实证明这比看起来更加混乱.在Java或C#中,您将声明IFunctor接口然后只写
List<IFunctor> l = new List<IFunctor> () {
new Just (1),
new List<Int>() {1,2,3},
new Nothing<Int>(),
new Right (5)
}
Run Code Online (Sandbox Code Playgroud)
假设Maybe,List并Either实施IFunctor.自然Just和Nothing延伸Maybe,并Right和Left延伸Either.不满意这个问题更容易解决这些语言!
在Haskell应该更干净的方式:(
luq*_*qui 14
在Haskell中,不允许向下转换.你可以使用AnyFunctor,但问题是没有任何方法可以回到你知道的仿函数.如果你有一个AnyFunctor a,你所知道的只是你有f a一些f,所以你所能做的就是fmap(让你另一个AnyFunctor).因此,AnyFunctor a实际上相当于().
您可以添加结构以AnyFunctor使其更有用,稍后我们会看到一点.
但首先,我将分享我可能最终在真实程序中执行此操作的方式:使用functor组合器.
{-# LANGUAGE TypeOperators #-}
infixl 1 :+: -- declare this to be a left-associative operator
data (f :+: g) a = FLeft (f a) | FRight (g a)
instance (Functor f, Functor g) => Functor (f :+: g) where
-- left as an exercise
Run Code Online (Sandbox Code Playgroud)
当数据类型读取时,f :+: g是一个函数,其值可以是f a或g a.
然后你可以使用,例如:
l :: [ (Maybe :+: []) Int ]
l = [ FLeft (Just 1), FRight [2,3,4], FLeft Nothing ]
Run Code Online (Sandbox Code Playgroud)
你可以通过模式匹配来观察:
getMaybe :: (Maybe :+: g) a -> Maybe a
getMaybe (FLeft v) = v
getMaybe (FRight _) = Nothing
Run Code Online (Sandbox Code Playgroud)
添加更多仿函数时会变得很难看:
l :: [ (Maybe :+: [] :+: Either Int) Int ]
l = [ FLeft (FLeft Nothing), FRight (Right 42) ]
-- Remember that we declared :+: left-associative.
Run Code Online (Sandbox Code Playgroud)
但我推荐它,只要你能处理丑陋,因为它跟踪类型中可能的仿函数列表,这是一个优点.(也许你最终需要的结构超出了Functor可以提供的范围;只要你可以提供它(:+:),你就处于良好的领域.)
您可以通过创建显式联合使术语更清晰,Ganesh建议:
data MyFunctors a
= FMaybe (Maybe a)
| FList [a]
| FEitherInt (Either Int a)
| ...
Run Code Online (Sandbox Code Playgroud)
但你必须Functor为它重新实施付出代价({-# LANGUAGE DeriveFunctor #-}可以提供帮助).我更喜欢忍受丑陋,并且在一个足够高的抽象层次上工作,它不会太丑陋(即一旦你开始编写FLeft (FLeft ...)它的时间来重构和概括).
如果你不想自己实现它,可以在comonad-transformers包中找到Coproduct(尽管这是很好的练习).其他常见的仿函数组合器位于变换器包中的Data.Functor.命名空间中.
AnyFunctor也可以扩展到允许向下转换.必须通过将Typeable类添加到您想要转发的任何内容来明确启用向下转换.每个具体类型都是Typeable; type constructors是Typeable1(1参数)的实例; 但它不是免费的类型变量,所以你需要添加类约束.所以AnyFunctor解决方案变成:
{-# LANGUAGE GADTs #-}
import Data.Typeable
data AnyFunctor a where
AnyFunctor :: (Functor f, Typeable1 f) => f a -> AnyFunctor a
instance Functor AnyFunctor where
fmap f (AnyFunctor v) = AnyFunctor (fmap f v)
Run Code Online (Sandbox Code Playgroud)
这允许向下转换:
downcast :: (Typeable1 f, Typeable a) => AnyFunctor a -> Maybe (f a)
downcast (AnyFunctor f) = cast f
Run Code Online (Sandbox Code Playgroud)
这种解决方案实际上比我预期的更清洁,可能值得追求.
一种方法是使用存在:
{-# LANGUAGE GADTs #-}
data AnyFunctor v where
AnyFunctor :: Functor f => f v -> AnyFunctor v
instance Functor AnyFunctor where
fmap f (AnyFunctor fv) = AnyFunctor (fmap f fv)
Run Code Online (Sandbox Code Playgroud)
您在问题中要求的输入列表是不可能的,因为它没有正确输入,所以有些包装AnyFunctor可能是必要的,但是你接近它.
您可以通过在AnyFunctor数据构造函数中包装每个值来创建输入列表:
[AnyFunctor (Just 1), AnyFunctor [1,2,3],
AnyFunctor Nothing, AnyFunctor (Right 4)]
Run Code Online (Sandbox Code Playgroud)
请注意,当您使用fmap (+1)它时,最好使用显式类型签名1来避免数字重载的任何问题,例如fmap (+(1::Integer)).
目前的困难AnyFunctor v在于你实际上无法用它做多少 - 你甚至无法查看结果,因为它不是一个实例Show,更不用说提取一个值以供将来使用.
把它变成一个实例是有点棘手的Show.如果我们Show (f v)向AnyFunctor数据构造函数添加一个约束,那么Functor实例就会停止工作,因为不能保证它会生成一个Show自己的实例.相反,我们需要使用一种"高阶"类型类Show1,如本答案所述:
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE GADTs #-}
data AnyFunctor v where
AnyFunctor :: (Show1 f, Functor f) => f v -> AnyFunctor v
instance Functor AnyFunctor where
fmap f (AnyFunctor fv) = AnyFunctor (fmap f fv)
data ShowDict a where
ShowDict :: Show a => ShowDict a
class Show1 a where
show1Dict :: ShowDict b -> ShowDict (a b)
instance Show v => Show (AnyFunctor v) where
show (AnyFunctor (v :: f v)) =
case show1Dict ShowDict :: ShowDict (f v) of
ShowDict -> "AnyFunctor (" ++ show v ++ ")"
instance Show1 [] where
show1Dict ShowDict = ShowDict
instance Show1 Maybe where
show1Dict ShowDict = ShowDict
instance Show a => Show1 (Either a) where
show1Dict ShowDict = ShowDict
Run Code Online (Sandbox Code Playgroud)
在ghci中,这给出了以下内容(为了便于阅读,我打破了这些行):
*Main> map (fmap (+1)) [AnyFunctor (Just 1), AnyFunctor [1,2,3],
AnyFunctor Nothing, AnyFunctor (Right 4)]
[AnyFunctor (Just 2),AnyFunctor ([2,3,4]),
AnyFunctor (Nothing),AnyFunctor (Right 5)]
Run Code Online (Sandbox Code Playgroud)
基本思想是表达类型构造函数的想法Nothing,[]或者Either a"保留" Show约束,使用Show1类来说明Show (f v)只要Show v可用就可用.
同样的技巧适用于其他类型类.例如,@ luqui的答案显示了如何使用Typeable已经具有内置Typeable1变体的类提取值.您添加的每个类型类都限制了您可以添加的内容AnyFunctor,但也意味着您可以使用它执行更多操作.
| 归档时间: |
|
| 查看次数: |
853 次 |
| 最近记录: |