artemave
artemave

Reputation: 6936

Haskell: scary type signature

I'm struggling to understand this type signature:

Prelude Text.Regex.Posix> :t (=~)
(=~)
  :: (Text.Regex.Base.RegexLike.RegexMaker
        Regex CompOption ExecOption source,
      Text.Regex.Base.RegexLike.RegexContext Regex source1 target) =>
     source1 -> source -> target

I reckon they list type classes, source, source1 and target should be instances of, but the semantics look completely arcane (that is, I would not be able to replicate it, even if I understood what it says).

Upvotes: 4

Views: 715

Answers (2)

ephemient
ephemient

Reputation: 204678

I wrote a fairly thorough description of the Text.Regex typeclasses in another answer.

Copying most of that here...


All the Text.Regex.* modules make heavy use of typeclasses, which are there for extensibility and "overloading"-like behavior, but make usage less obvious from just seeing types.

Now, you've probably been started off from the basic =~ matcher.

(=~) ::
  ( RegexMaker Regex CompOption ExecOption source
  , RegexContext Regex source1 target )
  => source1 -> source -> target
(=~~) ::
  ( RegexMaker Regex CompOption ExecOption source
  , RegexContext Regex source1 target, Monad m )
  => source1 -> source -> m target

To use =~, there must exist an instance of RegexMaker ... for the LHS, and RegexContext ... for the RHS and result.

class RegexOptions regex compOpt execOpt | ...
      | regex -> compOpt execOpt
      , compOpt -> regex execOpt
      , execOpt -> regex compOpt
class RegexOptions regex compOpt execOpt
      => RegexMaker regex compOpt execOpt source
         | regex -> compOpt execOpt
         , compOpt -> regex execOpt
         , execOpt -> regex compOpt
  where
    makeRegex :: source -> regex
    makeRegexOpts :: compOpt -> execOpt -> source -> regex

A valid instance of all these classes (for example, regex=Regex, compOpt=CompOption, execOpt=ExecOption, and source=String) means it's possible to compile a regex with compOpt,execOpt options from some form source. (Also, given some regex type, there is exactly one compOpt,execOpt set that goes along with it. Lots of different source types are okay, though.)

class Extract source
class Extract source
      => RegexLike regex source
class RegexLike regex source
      => RegexContext regex source target
  where
    match :: regex -> source -> target
    matchM :: Monad m => regex -> source -> m target

A valid instance of all these classes (for example, regex=Regex, source=String, target=Bool) means it's possible to match a source and a regex to yield a target. (Other valid targets given these specific regex and source are Int, MatchResult String, MatchArray, etc.)

Put these together and it's pretty obvious that =~ and =~~ are simply convenience functions

source1 =~ source
  = match (makeRegex source) source1
source1 =~~ source
  = matchM (makeRegex source) source1

Upvotes: 3

Tim Robinson
Tim Robinson

Reputation: 54724

There's nothing strange going on here: just some type classes with a lot of arguments. (The long Text.Regex.Base... module names don't help, either.)

  • There must be a RegexMaker instance for: Regex, CompOption, ExecOption, and whatever type source is
  • There must be a RegexContext instance for: Regex, whatever type source1 is, and whatever type target is
  • The (=~) function itself takes a source1 and a source and gives a target

Haskell's own (+) operator is a similar shape to (=~), but its type is hopefully easier to read:

(+) :: Num a => a -> a -> a

Upvotes: 4

Related Questions