3
votes

Some of my data types in Haskell end up having quite a few records, for example

data MyData
  = A Int Int String Float Int (Char, Char)
  | B Int String Float (Char, String) String

and to check what type it is I end up writing functions like

isA :: MyData -> Bool
isA (A _ _ _ _ _ _) = True
isA _ = False

-- isB and so on

Now this gets cumbersome fairly quickly so I searched how to use derive or something similar to auto-generate those and I found this. However, the library proposed in that question seems to be out of date due to the introduction of ghc generics and the related generic-deriving library. I've taken a look at these but they seem to be very powerful and I don't quite know where to start.

So my question is: How can you (if possible) get around having to manually write isA and isB for MyData?

2
Relevant Reddit thread: reddit.com/r/haskell/comments/74r611/deriving_is_functions. Although… I’m not sure what about those makes the library you’ve already found out of date? - Ry-♦
The fact that its build status is 'failed' and the comment right beneath that. Maybe it is not accurate to call it "out of date" tough, I'm not sure. - ryan91
Would it help if you would write it this way: data MyData = A (Int, Int, String, Float, Int, (Char, Char)) | B (Int, String, Float, (Char, String), String)? - Elmex80s
Tangential note: while this is an entirely reasonable question, you might want to double-check whether and how often you actually need the boolean tests. For instance, an if isA x -- etc. check can generally be replaced with pattern matching on x (say, with a case expression). - duplode

2 Answers

9
votes

Record syntax can be exploited for this.

isA :: MyData -> Bool
isA A{} = True
isA _   = False

This requires no extensions, and works regardless of the number of parameters constructor A takes.

(That being said, I don't think such functions are that useful in Haskell. Beware of "boolean blindness" -- i.e., of reducing information to booleans when you do not need to.)

0
votes

So one way that circumvents the mindless writing of a lot of type check functions is to use this library (found in the Reddit Ry posted - thanks!).

Example code:

import Data.Generics.Is.TH

-- Cumbersome
printType (A _ _ _ _ _ _) = putStrLn "Is A"
printType (B _ _ _ _ _) = putStrLn "Is B"

-- With library
printType x
  | $(is 'A) x = putStrLn "Is A"
  | $(is 'B) x = putStrLn "Is B"

Instead of isA and isB you would simply write $(is 'A) or $(is 'B) which works fine for me.