Implicit parameters as a replacement for reader monad - Haskell

Welcome to the Functional Programming Zulip Chat Archive. You can join the chat here.

Julian KG

I started using implicit parameters the way you would use reader monad. There are a lot of benefits to this approach: not having to deal with multiple monads. Implicit parameters has all of the benefits of Has pattern without any of the cruft. Honestly I'm not seeing any downside.

Georgi Lyubenov // googleson78

I can't open the links to the supposed problems with them

Georgi Lyubenov // googleson78

referenced in "the reflection paper"

TheMatten

It think problems boil down to examples like this:

λ> let ?a = 1 in let b :: Int = ?a + 1 in let ?a = 42 in b
43
λ> let ?a = 1 in let b :: Int; b = ?a + 1 in let ?a = 42 in b
2
TheMatten

It may not be apparent at first sight which "instance" should be chosen

TheMatten

(It makes a lot of sense from typechecker perspective, but may be hard to spot for human)

Asad Saeeduddin

A term's behavior can change if it's signature is added, removed, or changed

I can't remember the last time I wrote a Haskell library/program where this wasn't the case, and I never use implicit parameters

Georgi Lyubenov // googleson78

@Asad Saeeduddin
I don't think "common" haskell programming does this very often - anything without fancy type classes/type families should be fine, no?

TheMatten

It's not that much of a problem with "normal classes", because they are globally coherent (usually)

Asad Saeeduddin

well what's a fancy typeclass? e.g. is this fancy?

class Foo a b
  where
  foo :: a -> b

test :: Foo Int Int => Int
test = foo $ (1 :: Int)
Georgi Lyubenov // googleson78

In my opinion, it is. I have no idea how to reason about anything like foo :: Foo a b => a -> b

This does indeed remind me of an annoying "everyday" example though - aeson's FromJSON typeclasses, which has a polymorphic return type

Asad Saeeduddin

you can't reason about Foo, it's just a made up typeclass with no rules. my point is in contravariant position the type of arguments is not always determined: you often have to fix it using type annotations or visible type applications

Asad Saeeduddin

if we only ever did things like test :: Foo Int Int => Int -> Int; test = foo, then everything would be fine, but we often apply functions to polymorphic arguments where the input type doesn't wholly determine the result type, and so we need to fix the argument type manually in order to resolve the right instance

Georgi Lyubenov // googleson78

my point is that Foo is indeed complicated from my pov, exactly because it has an entirely polymorphic argument/return-type

Georgi Lyubenov // googleson78

typeclasses like that lead to subtle errors, or at least I've made such mistakes with e.g. FromJSON

Julian KG

@TheMatten this example seems very contrived, and if I have to choose between this complexity, and the complexities that arise from the reader monad I choose this.

Asad Saeeduddin

@Georgi Lyubenov // googleson78 I think the bar for fancy/complicated is a bit too low then. If you try to model something like a class for vector spaces, like so:

class ... => VectorSpace s v
  where
  scale :: s -> v -> v

you will have to annotate/visibly type apply the scalar type if you use a numeric literal so as to choose the right instance. this is not a very exotic use case IMO

TheMatten

From this perspective, something like Agda's open on records seems to be superior to instance resolution in terms of clarity, even if it's more verbose

@Julian KG I would say adding type signature is actually something you may do pretty often when working with highly-polymorphic sub-bindings (e.g. in where)

Julian KG

@TheMatten I have an idea for a language with better support for implicit parameters in my head because there have been some things that I think could be more ergonomic. I'll have to read this paper and try out Agda so I see what else is out there.

Julian KG

@TheMatten
the code example that went like this:

sort1 :: Compares a ⇒ s→[a]→[a] --ok
sort2 :: Compares a ⇒[M sa]→[M sa] --ok

in the paper is exactly what I'd like to avoid. I think there is a way to achieve monomorphism without adding these M's or s ->'s.

Julian KG

it might not work as a Haskell extension, and it definitely wouldn't be as powerful as implicit parameters

Will

Georgi Lyubenov // googleson78 said:

2020-07-19-125852_483x193_scrot.png

these are problems with implicit parameters as a language extension, no? my read was that oleg's typeclass-based implicit parameter passing implementation doesn't suffer these drawbacks

Julian KG

@Will yeah the typeclass based approach does not have those problems, but it does create lots and lots of boilerplate and thus encourages sparing use.