I'm relatively new to Haskell. Here is what i want to do:
I want parse strings to chesspieces. my code is pretty straightforward so i hope it will explain itself:
data ChessPiece = King | ... etc
data DraughtPiece = DKing | ... etc
data Player = Black | White deriving (Show, Eq)
data Piece a = Piece (a, Player)
So a Piece can be of either 2 games, of either 2 players. I want a string "k" or "K" to respectively parse to a Black or White King, so I made this:
class Parse a where
parse :: String -> a
instance Parse ChessPiece where
parse a = case a of
"k" -> King
Everything fine so far.. i can call > parse "k" :: ChessPiece
. this works!
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x, White)
| otherwise = Piece (parse $ lowercase x, Black)
This has to work for either piece. The rule for uppercase and lowercase goes for DraughtPiece and for ChessPiece. How do i tell Haskell to parse the x to the correct type (a). Leaving out the cast for parse x
gives me non-exhaustive pattern error, changing it to parse x :: a
gives me 'could not deduce (Parse a1) from a use of 'parse' from the context (Parse a)'
How do i tell Haskell to parse "K" :: Piece ChessPiece
to (parse "k" :: ChessPiece, Black)
to (King, Black)
?
How do i tell Haskell to parse the x to the correct type (a).
If you're referring to
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x, White)
| otherwise = Piece (parse $ lowercase x, Black)
you don't need to. The type at which the recursive call to parse
is made is determined from the context, it's the parameter type a
.
But when you want to parse anything, there must be enough context to tell GHC what type the result shall have. In typical programmes, that can usually be determined from the context. If you have let p = parse input
and then later use p
in a context that requires p
to have a certain type, that tells the compiler which type shall be parsed. But at the ghci prompt, there is no such context, and you must explicitly tell ghci what type you want
ghci> parse "K" :: Piece ChessPiece
Leaving out the cast for 'parse x' gives me non-exhaustive pattern error,
If you try to call parse
with an input string you have not explicitly handled, you will get a non-exhaustive pattern error. The compilation alone will give you a warning about it (if you ask for warnings), eg
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') : (_ : _)
which means that in the instance Parse Piece
, you only defined parse
for the single input string "k"
. You should of course provide definitions for other input strings (including an explicit catch-all branch calling error
for invalid strings).
changing it to 'parse x :: a' gives me 'could not deduce (Parse a1) from a use of 'parse' from the context (Parse a)
That's a somewhat non-obvious thing. Type variables are implicitly forall-quantified, so when you write
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x :: a, White)
the a
in the definition of parse
is a fresh forall-quantified type variable, and you effectively say that the first component of the pair can have any type, as if you said
instance Parse a => Parse (Piece a) where
parse x | isLowercase x = Piece (parse x :: anyType, White)
and of course, there is no instance Parse anyType
that can be inferred from the context Parse a
.
You can tell GHC that the a
in the tuple should denote the same type as the one in the instance head by using the ScopedTypeVariables
extension, but it's better to leave that for the time being.
You need ScopedTypeVariables
pragma to allow use of things like parse x :: a
where you have used typevariable a
in the function type.
You can also use FlexibleInstances
to define instance for Piece ChessPiece
and Piece DraughtPiece
if you want to something more specific for each case.
instance Parse (Piece ChessPiece) where
parse x = -- return Piece ChessPiece here
instance Parse (Piece DraughtPiece) where
parse x = -- return Piece DrauPiece here
In any case ghc needs enough context to know what type to parse to so you will need something like parse "k" :: Piece ChessPiece
Since your DraughtPiece
is pretty much the same as ChessPiece
, I would suggest you put them into the same data type for the same reason you don't invent a new Maybe
type every time Nothing
seems to be the wrong word . Another bonus is that the code scales much better when you want to increase the amount of games.
data ChessPiece = King | ... etc
data Game = Game Int
data Player = Black | White
data Piece = Piece ChessPiece Player Game
You now have to adjust your data representation. If you can adjust the representation of your file to be parsed, you could encode "black king of board n" as 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
A final note: the main difficulty of your problem is because your data isn't stored atomically: one token contains information about both color and type of a figure. When you're designing your own files, try to keep separate things separate.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.