gpt4 book ai didi

haskell - Haskell 中是否有通配符类型变量?

转载 作者:行者123 更新时间:2023-12-03 23:33:52 25 4
gpt4 key购买 nike

我希望能够多态地处理多个 Either值(value)观相同的人 Left类型,但不一样 Right类型,例如:

foo = Left "foo" :: Either String Int
bar = Left "bar" :: Either String Char
list = [foo, bar]
Left value 表示计算失败时的错误消息,而 Right value 根据不同的计算有不同的类型。如果失败,我只关心收集左侧的错误,因此能够为此目的对所有错误进行相同处理会很有用。
但是,这失败了,编译器希望两个类型变量都匹配:
    * Couldn't match type `Char' with `Int'
Expected type: Either String Int
Actual type: Either String Char
* In the expression: bar
In the expression: [foo, bar]
In an equation for `list': list = [foo, bar]
经过大量搜索,我唯一偶然发现的是称为“部分类型签名”的东西,它使用“_”通配符,并且必须通过标志显式启用才能编译。但是,即使启用它,它似乎仍然没有任何改变:
foo = Left "foo" :: Either String Int
bar = Left "bar" :: Either String Char
list = [foo, bar] :: Either String _
我收到同样的错误:
    * Couldn't match type `Char' with `Int'
Expected type: Either String Int
Actual type: Either String Char
* In the expression: bar
In the expression: [foo, bar] :: Either String _
In an equation for `list': list = [foo, bar] :: Either String _
在 Java 中(如果它有 Either 类型),我可以简单地做这样的事情:
List<Either<String, ?>> list = asList(foo, bar);
它会工作得很好,输入安全等等。
Haskell中是否有类型变量通配符对应 ?在 java ?如果不是,您如何处理您不关心某个类型变量并想对其进行抽象的情况?有替代方案吗?
更多详情:
意识到我对我的问题不够清楚,我将详细说明:
我试图实现的是一个命令行解析器。它应该接收用户的输入(命令行参数)并返回一个 Params值,其中:
data Params = Params String Double Bool
为简单起见,我在上面的值构造函数中只包含了 3 个值,但实际上还有更多。
由于有许多不同的参数,每个参数都可能以自己的方式失败——用户可能没有提供必要的参数,提供的参数无效,等等。
只有在 时,解析才能成功全部 提供的参数是有效的。如果其中任何一个无效,则整个计算都应该失败。于是我想到了下面的解决方案(再次简化,所以请忽略效率等其他问题):
parse :: [String] -> Either [String] Params
parse args = if successful
then Right (Params strParam doubleParam boolParam)
else Left errors
where successful = null errors
(Right strParam) = eitherStr
(Right doubleParam) = eitherDouble
(Right boolParam) = eitherBool
errors = lefts [eitherStr, eitherDouble, eitherBool]
eitherStr = parseStr args
eitherDouble = parseDouble args
eitherBool = parseBool args

parseStr :: [String] -> Either String String
parseDouble :: [String] -> Either String Double
parseBool :: [String] -> Either String Bool
但它不能编译,因为我无法处理 eitherStr , eitherDoubleeitherBool具有相同的类型,如果有通配符类型变量,这是可能的。因此我的问题。
在目前的情况下,我不得不求助于这样的事情:
parse :: [String] -> Either [String] Params
parse args = if successful
then Right (Params strParam doubleParam boolParam)
else Left errors
where successful = null errors
(Right strParam) = eitherStr
(Right doubleParam) = eitherDouble
(Right boolParam) = eitherBool
errors = concat [strErrors, doubleErrors, boolErrors]
strErrors = getErrors eitherStr
doubleErrors = getErrors eitherDouble
boolErrors = getErrors eitherBool
eitherStr = parseStr args
eitherDouble = parseDouble args
eitherBool = parseBool args
getErrors = lefts . (:[])

parseStr :: [String] -> Either String String
parseDouble :: [String] -> Either String Double
parseBool :: [String] -> Either String Bool
可行,但更麻烦。当然,我的解决方案可能完全偏离轨道,并且有更好的惯用功能解决方案,我会很高兴听到的。
据说Java有 Object作为通用上限,因此比较具有误导性。但在我看来,理论上限总是存在的,即使在最坏的情况下,上限意味着您无法使用的值。这对于你不需要它的情况来说很好,比如我的。
尝试存在类型后:
存在类型对我没有帮助。他们定义了一种包装旧类型的新类型,因此接受 Either 的函数不会接受新类型,也不能重用。所以不要使用现有的 lefts函数,除了定义新类型的麻烦之外,我还必须自己为新类型实现它:
data SomeEither l = forall r. SomeEither (Either l r)
data Params = Params String Double Bool deriving (Show)

parse :: [String] -> Either [String] Params
parse args = if successful
then Right (Params strParam doubleParam boolParam)
else Left errors
where successful = null errors
(Right strParam) = eitherStr
(Right doubleParam) = eitherDouble
(Right boolParam) = eitherBool
errors = lefts' [SomeEither eitherStr, SomeEither eitherDouble, SomeEither eitherBool]
eitherStr = parseStr args
eitherDouble = parseDouble args
eitherBool = parseBool args

lefts' :: [SomeEither l] -> [l]
lefts' [] = []
lefts' ((SomeEither (Left l)):xs) = l:(lefts' xs)
lefts' (x:xs) = lefts' xs

parseStr :: [String] -> Either String String
parseDouble :: [String] -> Either String Double
parseBool :: [String] -> Either String Bool

重点是做更少的工作并拥有更干净的代码。在这种情况下,存在类型的使用正好相反。

最佳答案

这是徒劳的。 Either L R哪里R未知只是Maybe L .如果您有 Right (r :: R)并将其放入您的列表中,从而忘记了 R是,那么即使你以后可以拿那个 Right r出了列表,你将不知道 r 的类型.这使它无法使用。 Java 中也基本相同:a Either<L, ?>真的只是一个 Optional<L> .如果EitherRight ,好吧,你得到的只是一个 Object (通配符的上限)。无视“万能”Object Java 中的操作(最重要的是 instanceof== ,还有 equalshashCodewaitsynchronized 等),这些都不存在(Haskell 运行时缺少身份值)类型),这个毫无特色的 Blob 你无能为力。为什么要把它放在首位?
现在,至于语法:_ Haskell 类型中的通配符是您不知道那里是什么类型的标记,并且您希望 GHC 推断它。我可以写:

something :: [a] -> _
something = foldr (:) []
GHC 会告诉我应该填写 _[a] ,因为这是 something 的类型. _不允许任何“新的”;当您不知道某物的类型是什么但为了清楚起见而想将其写出时(或者当您不想写出类型时,因为它很大,但这种情况很少见),这只是一种便利。在您的情况下, [foo, bar]类型错误,句号。列表必须是同类的(包含相同类型的元素)并且 [foo, bar]根本无效,并要求 GHC 告诉您当首先没有类型时该类型对您没有帮助。
相当于Java的 ?类型是存在类型:
-- SomeEither l = Either<L, ?>
data SomeEither l = forall r. SomeEither (Either l r)
A SomeEither l (在 LHS 上声明)值是(根据 RHS)一对,由类型 r :: Type 组成和类型为 Either l r 的值.这也是 Either<L, ?>的意思在 Java 中: Either<L, ?>是由类型 R 组成的对的类型(滥用 Java 术语, R 是“捕获类型”)和值 Either<L, R> .但是,在 Java 中,这些对的打包和解包是隐式的(打包称为“向上转换”,解包称为“通配符捕获”)。在 Haskell 中,你不得不说
list = [SomeEither foo, SomeEither bar]
但现在我们回到我的第一点,现在你永远不能使用 Right再一次:
forM_ list (\(SomeEither x) ->
case x of
Left l -> putStrLn l
Right r -> putStrLn ":(") -- don't know type of r; can't really use it for anything!
因此,如果您必须在值进入列表之前对其进行预处理,并且如果没有必要保留 Right周围的值,然后在开始时摆脱它们:
list :: [Maybe String]
list = [left foo, left bar]
where left = either Just (const Nothing)
-- or maybe you just want
list :: [String]
list = catMaybes [left foo, left bar]
where left = either Just (const Nothing)
编辑:“将东西放入列表”是 100% 不是您编辑的问题的解决方案。请注意,即使您有 Java 中的通配符类型,它们也无济于事。我想你真的想要这个 Applicative (几乎是 WriterT [String] Maybe ):
newtype Validated a = Validated { runValidated :: Either [String] a }
deriving Functor
instance Applicative Validated where
pure = Validated . Right
Validated l <*> Validated r = Validated (l <!> r)
where
Left xs <!> Left ys = Left (xs ++ ys)
Left xs <!> Right _ = Left xs
Right _ <!> Left xs = Left xs
Right f <!> Right xs = Right (f xs)

parseStr :: [String] -> Validated String
parseDouble :: [String] -> Validated Double
parseBool :: [String] -> Validated Bool
parse :: [String] -> Validated Params
parse args = Params <$> parseStr args <*> parseDouble args <*> parseBool args
(遗憾的是,它不是 Monad 。)没有删除类型,没有什么花哨的。只需组合所有错误并在没有错误时生成一个值。

关于haskell - Haskell 中是否有通配符类型变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64140299/

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