我想定义一个类型,它包含一个容器和容器的类型,以及一些操作,比如对容器执行外积,Map容器及其值,以及各种标准类(Functor
、Applicative
、Monad
、MonadTrans
等)提供的所有内容。
这是类型的定义和我提到的两个操作:
-- metacontainer
newtype MC c a = MC { runMetaContainer :: c a } deriving Show
-- outer product
cross :: (Functor f1, Functor f2) => (a -> b -> c) -> MC f1 a -> MC f2 b
-> MC (Compose f1 f2) c
cross f (MC c1) (MC c2) = MC $ Compose $ (\x -> f x <$> c2) <$> c1
-- map the container
hoist :: (c1 a -> c2 b) -> MC c1 a -> MC c2 b
hoist f (MC c) = MC $ f c
这种方法是有效的,但是当我cross
两个MC
时,得到的容器是由一些嵌套的 * 不必要的 * 容器组成的,比如Compose
或Identity
。
请看这个例子:
let list = MC [1,2,3] :: MC [] Int
let justStr = MC $ Just "hey" :: MC Maybe String
let n = MC $ Identity 5 :: MC Identity Int
let dblLst = cross (*) list n :: MC (Compose [] Identity) Int
let justStrList = cross (\x str -> str ++ " " ++ show x)
dblLst justStr :: MC (Compose (Compose [] Identity) Maybe) String
因为justStrList
的类型中有那些不必要的Compose
s和Identity
s,所以我不能简单地做类似于...
hoist (\lstMbStr -> lstMbStr & filter isJust & map fromJust) justStrList
-- error
-- • Couldn't match type ‘Compose (Compose [] Identity) Maybe’
-- with ‘[]’
-- Expected: MyType [] (Maybe b0)
-- Actual: MyType (Compose (Compose [] Identity) Maybe) String
有什么方法可以将容器"标准化"吗?
在本例中,我想将(Compose (Compose [] Identity) Maybe) String
转换为等效的[Maybe String]
。
我试过用夏丽瑶的方法,但是对我不起作用。
这是我的全部代码:
import Data.Maybe ( catMaybes )
import Data.Functor.Identity ( Identity(..) )
import Data.Functor.Compose ( Compose, Compose(..) )
import Data.Coerce (coerce, Coercible)
-- metacontainer
newtype MC c a = MC { runMetaContainer :: c a }
-- outer product
cross :: (Functor f1, Functor f2) => (a -> b -> c) -> MC f1 a -> MC f2 b
-> MC (Compose f1 f2) c
cross f (MC c1) (MC c2) = MC $ Compose $ (\x -> f x <$> c2) <$> c1
-- map the container
hoist :: (c1 a -> c2 b) -> MC c1 a -> MC c2 b
hoist f (MC c) = MC $ f c
coerceMC :: Coercible (c a) (d a) => MC c a -> MC d a
coerceMC = coerce
main :: IO ()
main = do
let list = MC [1,2,3]
let justStr = MC $ Just "hey"
let n = MC $ Identity 5
let dblLst = cross (*) list n
let justStrList = cross (\x str -> str ++ " " ++ show x) dblLst justStr
print $ hoist catMaybes (coerceMC justStrList)
这是我收到的错误信息
error:
• Couldn't match type: [Char]
with: Maybe b0
Expected: MC (Compose (Compose [] Identity) Maybe) (Maybe b0)
Actual: MC (Compose (Compose [] Identity) Maybe) [Char]
• In the first argument of ‘coerceMC’, namely ‘justStrList’
In the second argument of ‘hoist’, namely ‘(coerceMC justStrList)’
In the second argument of ‘($)’, namely
‘hoist catMaybes (coerceMC justStrList)’
如果我试图向GHC询问coerceMC justStrList
的类型,它会说它是MC d0 [Char]
,其中'd0'是一个不明确的类型变量。
如果我尝试启用PartialTypeSignatures
扩展,它会说它无法匹配类型的表示:[Identity (Maybe [Char])]
与以下值的关系:d0 [Char]
.
我不知道如何表达我希望coerceMC justStrList
拥有的类型,它应该是MC [Maybe] String
,除非它是一个无效的类型,因为种类不匹配。
我尝试强制提升函数(hoist f (MC c) = MC $ f $ coerce c
)内部的容器,因为我知道如何表达c
的强制(从Compose (Compose [] Identity) Maybe String
到[Maybe String]
),但它仍然不起作用。
我设法让它与hoist
的定义一起工作:hoist f (MC c) = MC $ f (coerce c :: [Maybe String])
,但我希望找到一个解决方案,如果f
的类型存在,则可以从f
的类型推断强制类型。
1条答案
按热度按时间c9qzyr3d1#
可以使用
coerce
在具有相同运行时表示形式的类型之间进行Map。coerce
具有很强的多态性,通常需要类型注解。以下专门化可以修复一些类型参数,从而有所帮助:您的示例仍然需要更复杂一些,
justStrList
的类型是MC (Compose (Compose [] Identity) Maybe) String
,它简化为MC (Compose [] Maybe) String
,还剩下一些Compose
,这实际上无法进一步简化。实际上,最后一个
Compose
只能在hoist
中,在应用给定函数之前被消除。找到一个足够好的hoist
的泛化有点棘手。我会选择这个,它只需要一些围绕主函数的转换函数。由于前面提到的类型推断问题,你可能不希望两个都是
coerce
,一个更特殊的组合子在一方应用coerce
会有一个丑陋的、不对称的签名,所以hoistBetween
就是了。你现在可以这样写:
这仍然需要对结果进行类型签名,因为
print
是多态的,但是在更具体的示例中,您可能需要知道hoistBetween
的两侧。或者,这个
hoistBetween
还允许您只使用newtype构造函数和析构函数,避免了coerce
的类型推断问题(需要一点开销)。