这是一个非常标准的泛型编程用例。这是一个非常常见的用例,有些库不必编写通用实现。
import Generics.OneLiner
l :: [R]
l = gfoldMap (For :: For C) (pure . f) t :: [R]
gfoldMap 是数据类型字段的折叠,假设它们都是给定类型类的所有实例,允许一般收集结果,这里是C。
gfoldMap (For :: For C) :: (forall a. C a => a -> [R]) -> (A, B, B, A, B, A, B) -> [R]
请注意,这需要Generic 的实例,由基本包派生到最多7 个元组。
您可以定义自己的元组,并为其派生Generic。
出于各种原因,您可能希望保留数据类型的结构,而不是在列表中收集结果。在撰写本文时,one-liner 在这方面仍然有点僵化,因为它不处理“类型更改遍历”(从 (A, B, B, A, B, A, B) 到 (R, R, R, R, R, R, R))。
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Data.Profunctor
import Data.Profunctor.Product
import Data.Profunctor.Product.Default
我们定义了一种新类型,不会用孤儿污染环境。
newtype P a b = P { unP :: a -> b } deriving
(Profunctor, ProductProfunctor)
我们将f 声明为Default 映射A 和B 到R 的方式。
instance Default P A R where def = P f
instance Default P B R where def = P f
库隐式将此扩展到元组,将A 和B 的任何元组映射到R 的对应元组。
-- Type signature required
t'_ :: (R, R, R, R, R, R, R)
t'_ = unP def t
您可能不想键入类型签名。由于类型类恶作剧,无法推断。但是,它可以根据输入的类型来计算。因此,您可以定义一个类型族(类型级函数),它在元组类型中用R 替换出现的A 和B。实际上,任何看起来像F a b c d e f(其中F 是类型构造函数)的类型都将转换为F R R R R R R。
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE UndecidableInstances #-}
type family Rify (a :: k) where
Rify (f a) = Rify f a
Rify a = a
专门化def(它有一个类型推断不友好的类型)来使用这个家族:
defP :: Default P a (Rify a) => P a (Rify a)
defP = def
-- Type signature now optional
t' :: (R, R, R, R, R, R, R)
t' = unP defP t
产品推广者之二
你也可以通过选择正确的profunctor来获得一个列表。
顾名思义,product-profunctors 以一般方式与ProductProfunctor 一起工作。这个 profunctor 在列表中收集 R 值。它相当于Control.Applicative中的Const [R]。
newtype Q a b = Q { unQ :: a -> [R] }
instance Profunctor Q where dimap f _ (Q q) = Q (q . f)
instance ProductProfunctor Q where
purePP _ = Q (const [])
Q x **** Q y = Q (\a -> x a ++ y a)
-- Older versions of product-profunctor use these two instead.
empty = Q (const [])
Q x ***! Q y = Q (\(a, b) -> x a ++ y b)
-- or
--
-- newtype Q a b = Q (Star (Const [R]) a b)
-- deriving (Profunctor, ProductProfunctor)
--
-- unQ :: Q a b -> a -> [R]
定义默认操作。
instance Default Q A b where def = Q (pure . f)
instance Default Q B b where def = Q (pure . f)
再一次,这些被隐式组合成“遍历”元组。
-- The second parameter doesn't actually matter, but
-- the type-checker doesn't know it so we put something for it
-- to infer. Could be `Q a ()`, anything that's not ambiguous.
defQ :: Default Q a a => Q a a
defQ = def
t'' :: [R]
t'' = unQ defQ t
这实际上与 one-liner 在内部的工作方式非常相似,尽管它目前使用自己的 ProductProfunctor 风格。