Haskell: Get data constructor name as string
Asked Answered
R

3

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)

Roam answered 18/8, 2013 at 8:39 Comment(0)
R
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
Roam answered 18/8, 2013 at 8:50 Comment(2)
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.Georgettegeorgi
You can also use showConstr instead of show. They do the exact same thing but showConstr has less of a risk of being used on the wrong typeWhim
T
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"
Thermic answered 18/8, 2013 at 9:6 Comment(4)
You might also want to implement a custom show output that does not involve the constructor.Mccarty
show is lazy, so this probably won't be very slow. Notice how take 5 (show (Just undefined)) works fine.Badajoz
You mean words, not unwords. (Actually, I'd write something like takeWhile (/=' ') . show)Gaslit
if somebody modifies the way show is implemented by default it does not work... this solution relies on a conventionDisenable
C
-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"
Caste answered 19/11, 2021 at 11:48 Comment(1)
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.Doyle

© 2022 - 2024 — McMap. All rights reserved.