[英]Simulating existential quantification in function return types
有时候我需要返回存在量化类型的值。 当我使用幻像类型时(例如表示平衡树的深度),这种情况最常发生。 AFAIK GHC没有任何exists
量词。 它只允许存在量化的数据类型 (直接或使用GADT)。
举个例子,我想要这样的函数:
-- return something that can be shown
somethingPrintable :: Int -> (exists a . (Show a) => a)
-- return a type-safe vector of an unknown length
fromList :: [a] -> (exists n . Vec a n)
到目前为止,我有两个可能的解决方案,我将添加作为答案,我很高兴知道是否有人知道更好或不同的东西。
标准解决方案是创建一个存在量化的数据类型。 结果就像是
{-# LANGUAGE ExistentialQuantification #-}
data Exists1 = forall a . (Show a) => Exists1 a
instance Show Exists1 where
showsPrec _ (Exists1 x) = shows x
somethingPrintable1 :: Int -> Exists1
somethingPrintable1 x = Exists1 x
现在,可以自由使用show (somethingPrintable 42)
。 Exists1
不能是newtype
,我想这是因为有必要在隐藏的上下文字典中传递show
的特定实现。
对于类型安全的向量,可以以相同的方式创建fromList1
实现:
{-# LANGUAGE GADTs #-}
data Zero
data Succ n
data Vec a n where
Nil :: Vec a Zero
Cons :: a -> Vec a n -> Vec a (Succ n)
data Exists1 f where
Exists1 :: f a -> Exists1 f
fromList1 :: [a] -> Exists1 (Vec a)
fromList1 [] = Exists1 Nil
fromList1 (x:xs) = case fromList1 xs of
Exists1 r -> Exists1 $ Cons x r
这很好用,但我看到的主要缺点是额外的构造函数。 每次调用fromList1
导致构造函数的应用程序被立即解构。 和以前一样, newtype
是不可能的Exists1
,但我想没有任何类型的类约束的编译器可以允许它。
我基于rank-N continuations创建了另一个解决方案。 它不需要额外的构造函数,但我不确定,如果附加的函数应用程序没有添加类似的开销。 在第一种情况下,解决方案是:
{-# LANGUAGE Rank2Types #-}
somethingPrintable2 :: Int -> ((forall a . (Show a) => a -> r) -> r)
somethingPrintable2 x = \c -> c x
现在可以使用somethingPrintable 42 show
来获得结果。
并且,对于Vec
数据类型:
{-# LANGUAGE RankNTypes, GADTs #-}
fromList2 :: [a] -> ((forall n . Vec a n -> r) -> r)
fromList2 [] c = c Nil
fromList2 (x:xs) c = fromList2 xs (c . Cons x)
-- Or wrapped as a newtype
-- (this is where we need RankN instead of just Rank2):
newtype Exists3 f r = Exists3 { unexists3 :: ((forall a . f a -> r) -> r) }
fromList3 :: [a] -> Exists3 (Vec a) r
fromList3 [] = Exists3 (\c -> c Nil)
fromList3 (x:xs) = Exists3 (\c -> unexists3 (fromList3 xs) (c . Cons x))
使用一些辅助函数可以使这更具可读性:
-- | A helper function for creating existential values.
exists3 :: f x -> Exists3 f r
exists3 x = Exists3 (\c -> c x)
{-# INLINE exists3 #-}
-- | A helper function to mimic function application.
(?$) :: (forall a . f a -> r) -> Exists3 f r -> r
(?$) f x = unexists3 x f
{-# INLINE (?$) #-}
fromList3 :: [a] -> Exists3 (Vec a) r
fromList3 [] = exists3 Nil
fromList3 (x:xs) = (exists3 . Cons x) ?$ fromList3 xs
我看到的主要缺点是:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.