gpt4 book ai didi

haskell - 在函数返回类型中模拟存在量化

转载 作者:行者123 更新时间:2023-12-04 11:10:34 25 4
gpt4 key购买 nike

有时我遇到需要返回存在量化类型的值。当我使用幻像类型(例如表示平衡树的深度)时,这种情况最常发生。 AFAIK GHC 没有任何类型的 exists量词。它只允许 existentially quantified data types (直接或使用 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)

到目前为止,我有 2 个可能的解决方案作为答案添加,我很高兴知道是否有人知道更好或不同的东西。

最佳答案

标准解决方案是创建一个存在量化的数据类型。结果会是这样的

{-# 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 延续的解决方案。它不需要额外的构造函数,但我不确定额外的功能应用程序是否不会增加类似的开销。在第一种情况下,解决方案是:
{-# 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

我在这里看到的主要缺点是:
  • 附加功能应用程序可能产生的开销(我不知道编译器可以优化多少)。
  • 可读性较差的代码(至少对于不习惯延续的人来说)。
  • 关于haskell - 在函数返回类型中模拟存在量化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12107124/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com