Value Oriented Programming Needs Implicits?
matklad.github.io
matklad.github.io
Yeah, you're looking for typeclasses a la Lean; probably Agda and Coq as well but Lean (3 or 4) is really built around typeclass inference.
Basically "typeclasses" are arbitrary types (not necessarily "classes" which are basically vtables) which can appear as a special kind of implicit argument. They can be given explicitly, but when inferred, the type is looked up in both a global and a local table for a registered value. The way this works is you might have a class `dec_order : (T : Type) -> Type` with a function `lt : T -> T -> bool` and then you can write a function `sort : (T : Type) [inst : dec_order T] -> list T -> list T`. By declaring an `instance : dec_order int` then you can now sort lists of integers. But you could also pass an explicit argument to `sort` so that, as in the example in the article, you can sort a list of indices into some other list using a comparison function which closes over that list. It really is the best of both worlds, traits/interfaces vs explicit vtables.
But you probably wanted to skip the whole dependent types thing. So I guess I repeat the original question, but with the caveat "without dependent types"?
In 30 years and this will be the new great thing for 10 more after that. You heard it here first.
ETA: Oh, if you mean like rust trait coherence, then no. That would map to instances being unique, which is not the case. In addition to being able to pass arbitrary values as instance parameters, there is a priority system. But some effort is put into making sure that alternate implementations end up defeq, which is kind of like uniqueness in a way.
If I understand the question, then yes this is how Scala works. When you write
def sort[T: Comparable](items: Vec[T])
It desugars to
def sort[T](items: Vec[T])(implicit c: Comparable[T])
Here Scala allows multiple parameter lists, Comparable[T] is a dictionary of methods, and the fact that it's an implicit parameter means there are some rules the compiler will follow to find the instance from either global or local scopes (e.g. methods with implicit parameters will implicitly thread that into other methods they call with implicit parameters of the same type). You can also explicitly pass the dictionary if you want, e.g. `sort(names)(stringComparator)`.
In practice, there's also implicit classes that get used to implement extension methods, so you might write `names.sort` and have that desugar to `VecExtensions(names).sort(stringComparator)`.
In languages with monads (which is another formulation of effects, but reified into a concrete type - monads are to effects like Result is to exceptions), it's the Reader monad
Haskell has actual implicit parameters, but while they have a clever implementation they aren't used in practice because they have serious problems - they use type constraints which interacts with type inference in nontrivial ways https://chrisdone.com/posts/whats-wrong-with-implicitparams/
So.. in Haskell, people end up using the ReaderT pattern (which is like, you wrap your monad in a ReaderT transformer so you can have implicit parameters threaded everywhere in your program - making some sort of composable globals) https://www.fpcomplete.com/blog/2017/06/readert-design-patte...
And this is all about reducing the boilerplate of threading parameters to functions
Bonus: in Emacs lisp implicit parameters are everywhere, because the language uses dynamic binding https://www.emacswiki.org/emacs/DynamicBindingVsLexicalBindi... this means that any variable that is not a local variable implicitly becomes an implicit parameter (Haskell implicit parameters are a controlled form of dynamic binding). Needless to say this is a mess, but highly appropriate for a moddable environment, like a game or, in this case, an IDE. RMS gives this rationale
https://stackoverflow.com/questions/3786033/how-to-live-with...
> Some language designers believe that dynamic binding should be avoided, and explicit argument passing should be used instead. Imagine that function A binds the variable FOO, and calls the function B, which calls the function C, and C uses the value of FOO. Supposedly A should pass the value as an argument to B, which should pass it as an argument to C.
> This cannot be done in an extensible system, however, because the author of the system cannot know what all the parameters will be. Imagine that the functions A and C are part of a user extension, while B is part of the standard system. The variable FOO does not exist in the standard system; it is part of the extension. To use explicit argument passing would require adding a new argument to B, which means rewriting B and everything that calls B. In the most common case, B is the editor command dispatcher loop, which is called from an awful number of places.
> What's worse, C must also be passed an additional argument. B doesn't refer to C by name (C did not exist when B was written). It probably finds a pointer to C in the command dispatch table. This means that the same call which sometimes calls C might equally well call any editor command definition. So all the editing commands must be rewritten to accept and ignore the additional argument. By now, none of the original system is left!