have I coded myself into a corner? I have a data type that's near the entry point of my program
data Token = A String | B String deriving (Eq, Show, Read)
In a library module, I have
type Constructor token = String -> token
Back towards the entry point, I have something like
constructor :: Constructor Token
constructor = A
tokenA :: Token
tokenA = A "a"
tokenB :: Token
tokenB = B "b"
Somewhere in the middle, between entry point and library, what I want to use is
tokenUsesConstructor :: Constructor token -> token -> Bool
so that some things can be done to the A
s and different things can be done to the B
s. The following doesn't work ofc because parameters cannot be repeated:
tokenUsesConstructor constructor (constructor _) = True
tokenUsesConstructor constructor (_ _) = False
-- use.hs:21:35: error: Parse error in pattern: constructor
and, as the error shows (indicating the first of the two lines above), the problem is more fundamental than that, as is also shown by
tokenUsesConstructor constructor token =
case token of
constructor _ -> True
_ -> False
which gets the same error (the former function is just a sugared version of the latter anyway, I think). If I could get the string, I could construct and compare but that's effectively the same problem with respect to the matching.
Can an instance of a data type be deconstructed without pattern-matching? I'd rather not add more to the data definition or turn it into a record type.
(BTW, I have got around this by creating an arbitrary token from the constructor, showing the original and constructed terms and then comparing the heads of the words but it feels like a work-around.)
The constructor part of a pattern must be static; it can’t be a variable. You also can’t use the same variable multiple times in a pattern (“nonlinear” patterns), or use a global variable name as a pattern to try to match that thing by equality; a variable name in a pattern always matches anything, and binds it to a new local variable by that name.
Furthermore, functions are completely opaque and cannot be compared. Even if they could, that wouldn’t be the right thing here:
Constructor token
denotes any function of typeString -> token
, not necessarily one of the constructors of yourToken
type.If you want to do this check dynamically, you can make a separate tag type for tokens and compare against that:
If, as it seems, all of your constructors just take a
String
argument, then you can factor out the repetition with a little type algebra (t + t = 2 × t):Alternatively, you could have this function accept a predicate as an argument, then the test is trivial (and you might not even need a separate function):
There are more involved solutions, such as GADTs to make this “tag” known statically, or various ways of making things generic, but I think whether they’re appropriate depends on the details of your actual use case that you haven’t described.