Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nested ambiguously typed methods

Tags:

haskell

Say I have a class

class T where
    tag1 :: String
    tag2 :: String

With ambiguous types enabled, I could specify each of them in an instance:

instance T A where
    tag1 = "tag1"
    tag2 = "tag2"

If I want to make tag2 append something to tag1, I can define

instance T A where 
    tag1 = "tag1"
    tag2 = tag1 @A ++ " suffix"

This works great, but if I want tag2 to always append suffix to each tag1, I seem to have to specify the ambiguous call for each instance.

I understand the need for this, as tag1 from any instance would work for each call. However, is there any trick within haskell for me to specify it once only?

Something like

tag2 :: T a => String
tag2 = tag1 @a ++ " suffix"
like image 705
Allan W Avatar asked Apr 14 '26 10:04

Allan W


1 Answers

Your code currently does not compile since the type class has no type parameters, so I'm going to assume your code is actually (assuming AllowAmbiguousTypes is enabled)

class T a where
    tag1 :: String
    tag2 :: String

Now you can provide a default implementation for tag2:

class T a where
    tag1 :: String
    tag2 :: String
    tag2 = "tag2"

But this does not meet the requirement of adding the suffix to tag1.
We could try this(assuming TypeApplications is enabled):

class T a where
    tag1 :: String
    tag2 :: String
    tag2 = tag1 @a ++ "suffix"

Now this won't compile, and the compilation error will be

error: Not in scope: type variable `a'

and rightfully so, the type a isn't defined anywhere. However, we want to refer to the a in the head of the class, for this we need the language extension ScopedTypeVariables, and with that the code will compile and you'll get the results you expect (I suggest reading up the linked documentation)

Here's a full program that demonstrates the usage:

{-# LANGUAGE TypeApplications, AllowAmbiguousTypes, ScopedTypeVariables #-}

class T a where
  tag1 :: String
  tag2 :: String
  tag2 = tag1 @a ++ " suffix"

data A = A
data B = B

instance T A where
  tag1 = "tagA"

instance T B where
  tag1 = "tagB"
  tag2 = "tagB overriden"

main = do
  putStrLn $ tag1 @A
  putStrLn $ tag2 @A
  putStrLn $ tag1 @B
  putStrLn $ tag2 @B

And the output is:

> ./foo
tagA
tagA suffix
tagB
tagB overriden
like image 64
Mor A. Avatar answered Apr 17 '26 02:04

Mor A.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!