Iron Gremlin
Iron Gremlin

Reputation: 407

Trouble with rigid type variables

I'm trying to build a parser for a simplistic DSL.

I'm running into trouble with the following function:

procP ::  ((a->Bool) -> [a] -> Bool) -> String -> Comperator -> ([String] -> Bool)
procP q v c =
  case c of
      NumE   -> q ((==) . unMay . readDouble $ v) . mapMaybe readDouble
      NumNE  -> q ((/=) . unMay . readDouble $ v) . mapMaybe readDouble
      NumLTE -> q ((<=) . unMay . readDouble $ v) . mapMaybe readDouble
      NumLT  -> q ((<)  . unMay . readDouble $ v) . mapMaybe readDouble
      NumGTE -> q ((>=) . unMay . readDouble $ v) . mapMaybe readDouble
      NumGT  -> q ((>)  . unMay . readDouble $ v) . mapMaybe readDouble
      Exists -> q notBlanks
      DatE   -> q ((==) . unMay . p_date $ v ) . mapMaybe p_date
      DatNE  -> q ((/=) . unMay . p_date $ v ) . mapMaybe p_date
      DatLTE -> q ((<=) . unMay . p_date $ v ) . mapMaybe p_date
      DatLT  -> q ((<)  . unMay . p_date $ v ) . mapMaybe p_date
      DatGTE -> q ((>=) . unMay . p_date $ v ) . mapMaybe p_date
      DatGT  -> q ((>)  . unMay . p_date $ v ) . mapMaybe p_date
      TxtE   -> q (==v)
      TxtME  -> (q (==v)) . map (rTrim)
      TxtNME -> (q (/=v)) . map (rTrim)
      TxtNE  -> q (/= v) 

The breakdown here is as follows:

q is some function that works like any from prelude. There are a couple different variations, but the type signature should be more or less the same - given a predicate and a list, return a bool.

v is a string input for purposes of comparison, it helps construct the predicate.

c is a token type constructed by the parser that represents what type of comparison we should use in the predicate.

unMay is a stupid hack function to "unwrap" a Just value or error on Nothing.

readDouble and p_date are functions that take a String and return either a Maybe Day or Maybe Double, respectively.

The design intent here is that the we're using the Comparator token to tell us what type to change the input Strings to internally, so that I can choose whether to compare against a value as a String, Day, or Double, respectively.

Sort of like a super basic version of what Perl does with == vs eq, but with less coercion and more nasty error messages.

The problem I'm having is that this doesn't type check, because the predicate getting passed to q ends up presenting as type Double->Bool instead of a->Bool, and so I get an error about how a is a rigid type variable.

The error more or less makes sense to me, but I'm having a lot of trouble thinking of a way to get around the problem.

Does anyone have any advice on how to re-structure this?

Upvotes: 1

Views: 126

Answers (1)

Daniel Wagner
Daniel Wagner

Reputation: 152707

Turn on Rank2Types (e.g. by putting {-# LANGUAGE Rank2Types #-} at the top of your file) and write

--         vvvvvvvvv difference is here
procP ::  (forall a. (a->Bool) -> [a] -> Bool) -> String -> Comperator -> ([String] -> Bool)

Upvotes: 4

Related Questions