tags:

views:

184

answers:

2

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).

+4  A: 

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
Tim Robinson
I don't get it. Perhaps I'm too noob yet. I am in the middle of this chapter at the moment: http://book.realworldhaskell.org/read/efficient-file-processing-regular-expressions-and-file-name-matching.html
artemave
Do you understand how to use type classes themselves? That is, do you know the significance of `Num a` in my `(+)` type above?
Tim Robinson
Yes, this I understand
artemave
But I wouldn't if there were say `Num SomeOtherType a`
artemave
But `Num SomeOtherType a` isn't valid syntax, since the `Num` type class only accepts one type argument. The `RegexMaker` type class, on the other hand, accepts four type arguments, so that's why it looks different.
Tim Robinson
Ok, it is more clear how to read it now. However, if `Num a` makes perfect sense in the way that "type `a` must be instance of `Num`" then it is still unclear what would be the meaning of say `Class1 Class2 a` (looks like I can't get my head around some basic conception or something)
artemave
I had the same confusion the first time I saw a type class with more than one argument. I would read it as: `Num a` = "there must be an instance of `Num` with parameter `a`"... `Class1 Class2 a` = "there must be an instance of `Class1` with parameters `Class2` and `a`"
Tim Robinson
Things start taking shape. Thanks!
artemave
Np! Luckily this sort of multi-parameter type class (or at least the kind with 4 or more parameters) is comparatively rare.
Tim Robinson
+3  A: 

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
ephemient