1

In the spirit of the question I asked here, (Generically) Build Parsers from custom data types? @danidiaz supplied a solution that works beautifully.

I'd like to do a little more like this: if f is an instance of a HasParser, a list of f is also an instance of the HasParser. Or in Haskell, that is

instance (HasParser f) => HasParser [f] where
  getParser = many' (getParser @f)

Well, this would not compile. I get the following error message:

error: Not in scope: type variable ‘f’

I am unfamiliar with the syntax with these language extensions. In particular, the usage of @ with Proxy always confuses me a lot. Hope some one can show me where to find the best documentation to read.

Community
  • 1
  • 1
user2812201
  • 437
  • 3
  • 7

1 Answers1

2

You need ScopedTypeVariables for this.

{-# LANGUAGE ScoperTypeVariables, UnicodeSyntax #-}

instance ∀ f . HasParser f => HasParser [f] where
  getParser = many' (getParser @f)

(ASCII syntax instance forall f . HasParser ... also works.)

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
  • 1
    It is funny. with `forall f . ` the code failed to compile. But my code compiled as-is with turning on `ScopedTypeVariables` language extension. – user2812201 May 19 '17 at 15:41
  • Well a catch! The code compiles as long as no `data` has a `String` filed. Otherwise, the compiler complaints about "Overlapping instances for HasParser [Char]". This is confusing me, as I don't have `Char` as an instance of `HasParser`. See the code at https://pastebin.com/FYrQHa5w . Code works fine without the `String` part. – user2812201 May 19 '17 at 16:11
  • @user2812201 I believe the overlapping problem is caused by the `HasParser String` instance. In Haskell, strings are really lists of chars. Is switching to something like `Text` possible? If not, perhaps something like an OVERLAPPABLE pragma could work http://stackoverflow.com/questions/39215103/ghc-overlapping-instances-when-generalising-addition – danidiaz May 19 '17 at 16:21
  • @danidiaz I believe the problem is caused by the `HasParser String` instance too. I'm trying to understand the reason, though: I don't have `instance HasParser Char` anywhere in the code, but GHC seems to think `Char` is an instance of `HasParser`, why? – user2812201 May 19 '17 at 16:31
  • @user2812201 The problem is really the instance for `String`. GHC sees that there are two possible definitions it can use: the basic instance for `String` and the one you have defined for lists, and it can't choose. You can nudge him trough a OVERLAPPABLE pragma in the more general instance or an OVERLAPPING pragma in your more specific instance, the one for `String`. – danidiaz May 19 '17 at 16:38
  • @danidiaz Sorry for too many questions but I'm really trying to understand this issue here. Let's look at the second way to instantiate `String`: as an list of `Char`s. Does not this require `Char` to be an instance of `HasParser` in the first place? Is GHC making `Char` an instance of `HasParser` automatically? – user2812201 May 19 '17 at 16:45
  • 3
    @user2812201 This is because the way instance resolution works, which is less "smart" than commonly assumed. When selecting which instance to use, *only the instance head* is checked, *not* the preconditions! If *after* having commited to an instance the preconditions (`HasParser Char` in this case) can't be met, compilation simply fails. GHC doesn't use the absence of a precondition to reject potential instances or resolve an ambiguity. – danidiaz May 19 '17 at 16:55