我对Haskell比较陌生。这是我想要做的:
我希望解析字符串到象棋。我的代码非常简单,所以我希望它会解释自己:
data ChessPiece = King | ... etc
data DraughtPiece = DKing | ... etc
data Player = Black | White deriving (Show, Eq)
data Piece a = Piece (a, Player)
所以一场比赛可以是两场比赛,两场比赛。我想要一个字符串“k”或“K”分别解析为黑人或白人国王,所以我做了这个:
class Parse a where
parse :: String -> a
instance Parse ChessPiece where
parse a = case a of
"k" -> King
到目前为止一切都很好..我可以致电> parse "k" :: ChessPiece
。这有效!
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x, White)
| otherwise = Piece (parse $ lowercase x, Black)
这必须适用于任何一件作品。大写和小写的规则适用于DraughtPiece和ChessPiece。我如何告诉Haskell将x解析为正确的类型(a)。抛弃parse x
的演员表给出了非详尽的模式错误,将其更改为parse x :: a
使我'无法从上下文中使用'解析'推断(解析a1)(解析a) “
如何告诉Haskell parse "K" :: Piece ChessPiece
到(parse "k" :: ChessPiece, Black)
到(King, Black)
?
答案 0 :(得分:3)
我如何告诉Haskell将x解析为正确的类型(a)。
如果您指的是
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x, White)
| otherwise = Piece (parse $ lowercase x, Black)
你不需要。对parse
进行递归调用的类型是根据上下文确定的,它是参数类型a
。
但是当你想要解析任何东西时,必须有足够的上下文告诉GHC结果应该具有什么类型。在典型的程序中,通常可以从上下文中确定。如果您有let p = parse input
,然后在需要p
具有特定类型的上下文中使用p
,则告诉编译器应解析哪种类型。但是在ghci提示符下,没有这样的上下文,你必须明确告诉ghci你想要什么类型
ghci> parse "K" :: Piece ChessPiece
抛出'parse x'的演员表给出了非详尽的模式错误,
如果您尝试使用未明确处理的输入字符串调用parse
,则会出现非详尽的模式错误。单独编译会给你一个警告(如果你要求警告),例如
Pieces.hs:14:13: Warning:
Pattern match(es) are non-exhaustive
In a case alternative:
Patterns not matched:
[]
(GHC.Types.C# #x) : _ with #x `notElem` ['k']
(GHC.Types.C# 'k') : (_ : _)
表示在instance Parse Piece
中,您只为单个输入字符串parse
定义了"k"
。您当然应该为其他输入字符串提供定义(包括为无效字符串调用error
的显式catch-all分支)。
将其更改为'parse x :: a'会让我'无法从上下文中使用'parse'来推断(解析a1)(Parse a)
这是一个有点不明显的事情。类型变量是隐式的forall-quantified,所以当你写
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x :: a, White)
a
定义中的parse
是一个新的forall量化类型变量,你有效地说该对的第一个组件可以有任何类型,就像你说的那样
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x :: anyType, White)
当然,没有instance Parse anyType
可以从上下文Parse a
中推断出来。
你可以告诉GHC,元组中的a
应该使用ScopedTypeVariables
扩展名表示与实例头中的{{1}}相同的类型,但最好暂时保留它。
答案 1 :(得分:1)
您需要ScopedTypeVariables
pragma才能使用函数类型中使用了typevariable parse x :: a
的{{1}}之类的内容。
如果您想要针对每种情况更具体的内容,您还可以使用a
来定义FlexibleInstances
和Piece ChessPiece
的实例。
Piece DraughtPiece
在任何情况下,ghc都需要足够的上下文来知道要解析的类型,因此您需要instance Parse (Piece ChessPiece) where
parse x = -- return Piece ChessPiece here
instance Parse (Piece DraughtPiece) where
parse x = -- return Piece DrauPiece here
答案 2 :(得分:0)
由于您的DraughtPiece
与ChessPiece
几乎相同,我建议您将它们放入相同的数据类型,原因与您不创建新的Maybe
类型相同每次Nothing
似乎都是错误的字。另一个好处是,当您想增加游戏数量时,代码会更好地扩展。
data ChessPiece = King | ... etc
data Game = Game Int
data Player = Black | White
data Piece = Piece ChessPiece Player Game
您现在必须调整数据表示。如果您可以调整要解析的文件的表示形式,则可以将“黑棋王n”编码为nk
。
import Data.Char
instance Parse Piece where
parse x = case x of
[n,p] | isLower p -> Piece (parse [p]) White (parse [n])
| otherwise -> Piece (parse [p]) Black (parse [n])
_ -> [Parse error]
instance Parse ChessPiece where
parse [p] = case toLower p of
'k' -> King
...
_ -> [Parse error]
instance Parse Game where
parse = Game . digitToInt
最后一点:您问题的主要困难在于您的数据未以原子方式存储:一个令牌包含有关图形颜色和类型的信息。当您设计自己的文件时,请尝试将单独的内容分开。