25

Let us say we have

data D = X Int | Y Int Int | Z String

I wish to have a function getDConst

getDConst :: D -> String

that returns either "X", "Y", or "Z", according to the data constructor used for its input. Is there a generic way to write this without having to do case on every data constructor? (I am ok with solutions relying on Data.Typeable or something similar)

tohava
  • 5,344
  • 1
  • 25
  • 47

3 Answers3

26

Found the solution myself, but leaving this question to help others:

import Data.Data
data D = X Int | Y Int Int deriving (Data,Typeable)

let result = show $ toConstr (X 3) -- result contains what we wanted
tohava
  • 5,344
  • 1
  • 25
  • 47
  • 1
    If anyone else also gets an error with this: Try adding `{-# LANGUAGE DeriveDataTypeable #-}` to the beginning of your file. It's necessary in GHC when you derive Data and Typeable. – jplatte Apr 14 '15 at 16:37
11

If you don't want to use Typeable, you can also do this with Show.

getDConst :: D -> String
getDConst = head . words . show

Show will not output all the fields, because it is lazy. You can test it runing this code in ghci:

Prelude> data D = D [Int] deriving (Show)
Prelude> getDConst $ D [1..]
"D"
remdezx
  • 2,939
  • 28
  • 49
MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
  • You might also want to implement a custom show output that does not involve the constructor. – kqr Aug 18 '13 at 10:36
  • 1
    `show` is lazy, so this probably won't be very slow. Notice how `take 5 (show (Just undefined))` works fine. – Ben Millwood Aug 18 '13 at 15:06
  • 3
    You mean `words`, not `unwords`. (Actually, I'd write something like `takeWhile (/=' ') . show`) – Lynn Aug 19 '13 at 12:18
  • 4
    if somebody modifies the way show is implemented by default it does not work... this solution relies on a convention – Nicolas Henin Oct 30 '18 at 10:35
-1

I have a much basic answer to the question without going through imports or whatever. It's Just a simple mere function.

let's say I have the following data. The repetitive Int in the data definition is intentional because I will use the don't care symbol afterwards:

data YES_NO_CANCEL = YES Int | NO Int Int | CANCEL Int Int Int

then you can make a function as :

extractDataType :: YES_NO_CANCEL -> String
extractDataType (YES _) = "YES"
extractDataType (NO _ _) = "NO"
extractDataType (CANCEL _ _ _) = "CANCEL"
Daniel Wagner
  • 145,880
  • 9
  • 220
  • 380
superlinux
  • 476
  • 1
  • 4
  • 12
  • This answer reminds me of a joke: A mathematician takes a walk over a foggy field when suddenly a low-flying balloon comes in sight. A balloon passenger shouts down to him: "Sir, can you tell us where we are?" The mathematician shouts back: "Yes, of course. You are in a balloon." *Correct, but entirely useless.* – Andreas Abel Jan 25 '23 at 18:58