为什么我不能将String用作类型类?

时间:2013-12-27 20:51:20

标签: haskell

这是一个绝对的初学者Haskell问题。可能需要进行一些编辑。

给出以下代码:

import Data.Maybe
someFunction :: String b => Int -> Maybe b
someFunction x = Nothing

我收到错误:

Main.hs@2:18-2:26`String' is applied to too many type arguments
In the type signature for `someFunction':
  someFunction :: String b => Int -> Maybe b

我想明白为什么我会看到这个错误。

a Similar签名适用于:

import Data.Maybe
somethingElse :: Num b => Int -> Maybe b
somethingElse x = Nothing

Num和String之间的区别是什么? (可能是因为String不是类型类?)

我能否以与Num类似的方式为String创建速记? (可能是的?)

如果我遇到这种情况,如何才能找到正确的类型?

更新

为了说明我想要为String类型提供简写的原因:

somethingElse ::  String -> String -> String -> String
somethingElse x s t  =  t ++ s ++ x

编译,但我更愿意写:

somethingElse :: ??? a => a -> a -> a -> a
somethingElse x s t  =  t ++ s ++ x

3 个答案:

答案 0 :(得分:5)

更新回答:

在阅读了对@YellPika的回答的评论之后,抓住我之前所说的内容。如果要为String定义较短类型的同义词,您只需要

-- Not that this serves any useful purpose.
type Str = String

然后您可以在类型注释中使用同义词:

someFunction :: Int -> Maybe Str
someFunction x = Nothing

但是,实际上,这是一个坏主意。每个人都知道String代表[Char],因为它在标准库中。只有您知道同义词Str代表String。如果您关注长类型注释,可以使用Haskell的类型推断。

Num类型,而不是类型。 type class定义了为多种类型in an ad-hoc fashion提供的方法集合。主流语言中最接近的模拟是C ++标准库中的concept和原始STL的概念。 (但是,类型类是Haskell语言的本机结构,而C ++ / STL概念只存在于C ++程序员的头脑中。)


我之前说过:

(留待这里仅供参考)

您可能正在寻找-XOverloadedStrings扩展程序。这使得字符串文字的类型为IsString s => s

以下是ghci会话的示例:

> :set -XOverloadedStrings
> import Data.String
> import Data.ByteString
> import Data.Text
> :t "a"
"a" :: IsString a => a
> :i IsString
class IsString a where
  fromString :: String -> a
    -- Defined in `Data.String'
instance IsString ByteString
  -- Defined in `Data.ByteString.Internal'
instance IsString Text -- Defined in `Data.Text'
instance IsString [Char] -- Defined in `Data.String'

在Haskell源文件(* .hs)中,您可以通过在顶部添加来使用此扩展:

{-# LANGUAGE OverloadedStrings #-}
module What.Ever where

-- ...

注意:所有这些都假设您使用的是GHC。我还没有尝试过其他Haskell实现。

答案 1 :(得分:5)

该错误有点误导。字符串(定义为type String = [Char])没有类型参数,但您只给它一个(String b)。

但是,只能在约束中指定类型类(即在=>之前),因此无论您提供多少参数String,您尝试编译的都将无法编译。

您想要做的只是someFunction :: Int -> Maybe String


好吧,如果你想要一个速记,那么我想你可以这样做:

type S = String

someFunction :: Int -> Maybe S

虽然我认为这会使代码混淆不清。

作为旁注:类型类,用于为类型创建本地定义的缩写。


增加:

我只记得您可以使用类型相等约束来模拟缩写。

{-# LANGUAGE TypeFamilies #-} -- Or GADTs

somethingElse :: s ~ String => s -> s -> s -> s
somethingElse x y z = x ++ y ++ z

通过使用类型相等,“同义词”现在在本地定义,因此比定义type SomeOtherNameForStringThatIsShorterThan'String' = String更难以阅读。

但我仍然建议只写出完整的类型。

答案 2 :(得分:1)

原帖要求String的“简称”。但是,我对Num示例所做的事情实际上是函数的抽象。你已经让你的第一个somethingElse函数适用于其他类型的数字(双打,复合体等)!

在同样的意义上,您可以将Monoid类型类用于第二个somethingElse函数,如下所示:

import Data.Monoid

somethingElse :: Monoid a => a -> a -> a -> a
somethingElse x s t = t <> s <> x

所以你可以说

somethingElse "World!" ", " "Hello"

并获取

"Hello, World!"

<强>释

您要求String的类型类。在Haskell中没有类似的东西,因为String只是[Char]的另一个名称。

即使haskell中的[] 类型类。 Haskell中的类型类是一个帮助您提取具体数据类型的抽象行为的概念。但[]是具体的数据类型(尽管它有1个类型参数)。

根据您的使用,您对[]连接的能力很感兴趣。有一个类型类可以捕获这种行为。此类型类为Monoid。其mappend<>运算符与++的{​​{1}}完全相同,但它也适用于其他[]类型。例如,当与Monoid一起使用时,Sum Int会有效地总结其参数并为您提供总结果。

这就是您真正想要的:<>类型类为Monoid做正确的事情,并使您的上一个String函数具有预期的类型签名形式。

但请记住:somethingElse仅限Monoid ++运算符,如果您需要其他内容,则需要其他抽象。