2
votes

I have a function, dir_con :: (Int -> Dir)

I want to pattern match to find which specific constructor dir_con is. The data type is:

data Dir = Define Int
         | Equals Int 
         | Data Int  
         | LCset Int
         | NoArg Int

So, dir_con will either be Define, Equals etc. It is passed to a function and I want to pattern match like so:

case dir_con of
    NoArg -> Do something specific
    _     -> Do something for the rest

The compiler doesn't like that. Error message is Couldn't match expected type 'Int -> Dir' with actual type 'Dir'.

Surely NoArg is a constructor of type (Int -> Dir)? Does Haskell not allow this type of pattern match? I have to do this because the Dir constructors come from a map. Is there a suggestion on how I can treat NoArg differently?

3
You may want to refactor your datatype to move the current constuctors to an enumeration, e.g. something like data Dir = Dir DirType Int; data DirType = Define | Equals | Data | LCset | NoArg.hammar

3 Answers

4
votes

You cannot pattern match on functions. A value of type Dir is built by applying one of several constructors, but dir_con is a function of type Int -> Dir.

You probably want to apply the function before pattern matching:

case dir_con 7 of  -- `7` is just an arbitrary value I'm passing
  NoArg _ -> ...
  _       -> ...

In addition, you have to match on the argument of the NoArg constructor as well, or else you'll get another type error after adding the argument to dir_con.

In your concrete situation, you'll most likely not want to pass a literal integer, but perhaps an argument you're getting from elsewhere:

myfun n = ... case dir_con n of
                NoArg _ -> ...
                _       -> ...
5
votes

Two ways:

case dir_con of
    NoArg _ -> Do something specific
    _     -> Do something for the rest

You're matching the /value/ not the constructor function.

Or, usingr record syntax:

case dir_con of
    NoArg {} -> Do something specific
    _     -> Do something for the rest

which is good hygiene, as it is neutral with respect to the number of fields.

1
votes

If all your constructors are of type Int -> Dir, then I would suggest the same as @hammar: make the constructor type an enumeration:

data DirType = Define | Equals | Data | LCset | NoArg

data Dir = Dir DirType Int

Then you can probably refactor whatever needs to know what constructor it is to use a value of DirType there instead.

The essential problem with trying to determine what constructor dir_con :: Int -> Dir is is that not everything of that type is a constructor. For exampe:

dispatch :: Int -> Dir
dispatch 0 = Define 0
dispatch 1 = Equals 1
dispatch n = NoArg n

There is no good answer that you can get for "which constructor" dispatch is.

If Dir were parametric

data Dir a = Define a | Equals a | Data a | LCset a | NoArg a

then there is a type of Dir constructors; namely forall a. a -> Dir a (you need the {-# LANGUAGE RankNTypes #-} extension to work with such types). Such a type disallows shenanigans like dispatch which inspects its argument. So if you have:

dir_con :: forall a. a -> Dir a

then you can inspect its constructor by passing it a trivial value

case dir_con () of
    ...

Even though you only ever practically use a Dir Int, the polymorphism allows you to be more descriptive with your types.