gpt4 book ai didi

haskell - 如何使用 GADT 包装类型签名

转载 作者:行者123 更新时间:2023-12-02 04:43:38 25 4
gpt4 key购买 nike

背景

一般任务是挖掘(非常复杂的)记录列表,报告它们包含的内容。这意味着在列表上发明大量过滤器和映射并进行报告。

简化后的问题看起来像这样:

{-# LANGUAGE OverloadedStrings, DataKinds, ExistentialQuantification, GADTs #-}

import qualified Data.Map as Map
import Control.Monad (void)

data Rec = Rec
{ _rInt :: Int
, _rChar :: Char
} deriving (Show,Eq,Ord)

tRec = take 10 $ zipWith Rec [1..] ['a'..]

count :: (Ord a) => [b] -> (b -> a) -> (b -> Bool) -> Map.Map a Int
count list key pred' =
let fn a = Map.insertWith (+) (key a) 1 in
foldr fn Map.empty (filter pred' list)

report :: (Ord a, Show a) => [b] -> String -> (b -> a) -> (b -> Bool) -> IO ()
report list str key pred' = do
let c = count list key pred'
(putStrLn . (str ++) . show) c

例如:

λ: report tRec "count of characters with odd ints: " _rChar (odd . _rInt) 
count of characters with odd ints: fromList [('a',1),('c',1),('e',1),('g',1),('i',1)]

标准数据类型包装器

可以使用像这样的高阶类型包装器将各种报告很好地捆绑在一起(并为进一步重构做好准备):

data Wrap = WrapInt Int | WrapChar Char deriving (Show, Eq, Ord)

demoWrap = void $ sequence $
zipWith3
(report tRec)
["count of all ints: ","count of characters with odd ints: "]
[WrapInt . _rInt, WrapChar . _rChar]
[const True, odd . _rInt]

给出:

λ: demoWrap
count of all ints: fromList [(WrapInt 1,1),(WrapInt 2,1),(WrapInt 3,1),(WrapInt 4,1),(WrapInt 5,1),(WrapInt 6,1),(WrapInt 7,1),(WrapInt 8,1),(WrapInt 9,1),(WrapInt 10,1)]
count of characters with odd ints: fromList [(WrapChar 'a',1),(WrapChar 'c',1),( WrapChar 'e',1),(WrapChar 'g',1),(WrapChar 'i',1)]

GADT解决方案

为了消除包装器类型的丑陋,我认为 ADT/GADT 解决方案可能会有所帮助。

这是我的尝试:

-- GADTs attempt

data Useable where
MkUseable :: (Show a, Eq a, Ord a) => a -> Useable

wrap :: (Show a, Eq a, Ord a) => a -> Useable
wrap = MkUseable

instance Show Useable where
showsPrec p (MkUseable a) = showsPrec p a


-- this doesn't work
instance Eq Useable
-- where
-- (MkUseable a) == (MkUseable b) = a == b

instance Ord Useable
-- where
-- compare (MkUseable a) (MkUseable b) = compare a b

demoGADT = void $ sequence $
zipWith3
(report tRec)
["all ints:","odd chars:"]
[wrap . _rInt, wrap . _rChar]
[const True, odd . _rInt]

编译器(非常正确地)拒绝具有潜在不同类型的 Useable 的 Eq 和 Ord 实例。但目的并不是要将 Useable 与不同类型进行比较 - 它只是简单地包装任何 (Show a, Ord a) 类型,以便我可以将它们放入列表中。

所以两个问题:

本着上述标准包装器解决方案的精神,如何使用 GADT 来包装类型?

我错过了什么(更普遍的)- 是否有更简单的方法来功能性地查询数据?

最佳答案

这将需要更改您的原始函数,但使用 GADT 解决此问题的一种方法是包装整个键控函数而不是返回值。即

data Key b where
Key :: (Ord a, Show a) => (b -> a) -> Key b

count :: [b] -> Key b -> (b -> Bool) -> Map.Map a Int
count list (Key key) pred' =
let fn a = Map.insertWith (+) (key a) 1 in
foldr fn Map.empty (filter pred' list)

report :: [b] -> String -> Key b -> (b -> Bool) -> IO ()
report list str key pred' = do
let c = count list key pred'
(putStrLn . (str ++) . show) c

但是,现在的问题是我们 promise 从 count 返回一个 Map.Map a Int 但我们不知道 a 是什么可能是,因为它隐藏在存在的 Key 中。但是由于我们并不真正关心(至少在这个例子的范围内),我们可以将结果 Map 包装在另一个隐藏键类型的存在中。

{-# LANGUAGE StandaloneDeriving #-}

data CountMap where
CountMap :: (Ord a, Show a) => Map.Map a Int -> CountMap

deriving instance Show CountMap

并相应地更改count

count :: [b] -> Key b -> (b -> Bool) -> CountMap
count list (Key key) pred' =
let fn a = Map.insertWith (+) (key a) 1 in
CountMap $ foldr fn Map.empty (filter pred' list)

现在我们可以做

demoWrap = void $ sequence $
zipWith3
(report tRec)
["count of all ints: ","count of characters with odd ints: "]
[Key _rInt, Key _rChar]
[const True, odd . _rInt]

关于haskell - 如何使用 GADT 包装类型签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20276336/

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