This question originally arose during my work on a dynamic JS type validator that relies on dictionary passing style as a rather simple type class mechanism, but I think it also applies to Haskell or other languages having a type class mechanism.
At first I figured it wouldn't be too big a problem to allow several type class instances per type in a setting with dictionary passing style, because I have full control of the type class resolution. But the actual issue seems to maintain type safety not type class resolution, as I am going to demonstrate using the type validator. Since the code is type annotated it is language agnostic to some extend.
// please assume `Number` to be a non-polymorphic `Int` type
Sum.Monoid // Monoid<Number>
Prod.Monoid // Monoid<Number>
Function.Monoid // Monoid<b> -> Monoid<(a -> b)>
Function.Monoid(Prod.Monoid) // Monoid<(a -> Number)>
Function.Monoid(Prod.Monoid).append // (a -> Number) -> (a -> Number) -> a -> Number
When you apply these types type safety is compromized, because it is possible to just write the following contrieved expression without the validator complaining:
Function.Monoid(Prod.Monoid)
.append(inc) (inc) (Sum.Monoid.empty); // yields 1 ((0 + 1) * (0 + 1)) instead of 4 ((1 + 1) * (1 + 1))
In Haskell each instance has its own distinct type to prevent such nonsensical expressions. But having to convert from one type-wrapper to another can be tedious.
Is there a viable, type-safe alternative or is this the very reason why Haskell's designers chose the distinct-type-per-class-instance simplification?
Describing the static view of the system. Showing the collaboration among the elements of the static view. Describing the functionalities performed by the system. Construction of software applications using object oriented languages.
Association Association is a relationship that is established between two distinct classes through their objects.
the statement which is true about class diagram is that it depicts the relationship between the classes.
The Java language is designed to enforce type safety. Anything in Java happens inside an object and each object is an instance of a class. To implement the type safety enforcement, each object, before usage, needs to be allocated.
(For this discussion, type safety specifically refers to memory type safety and should not be confused with type safety in a broader respect.) For example, type-safe code cannot read values from another object's private fields. Robin Milner provided the following slogan to describe type safety: Well-typed programs cannot "go wrong".
However, a number of very common operations are non-type-safe; for example, the usual way to print an integer is something like printf ("%d", 12), where the %d tells printf at run-time to expect an integer argument.
Type-safe code accesses only the memory locations it is authorized to access. (For this discussion, type safety specifically refers to memory type safety and should not be confused with type safety in a broader respect.) For example, type-safe code cannot read values from another object's private fields.
Perhaps one possibility would be to have some sort of scoped instance mechanism. Sort of half-baked, but imagine writing something like this:
with Prod.Monoid, Function.Monoid {
append(inc)(inc)(empty)
}
(Say with X, Y, Z {e}
is short for with X {with Y {with Z {e}}}
.) Presumably it would then be an error to introduce a second instance for a given class/type pair within that scope:
with Prod.Monoid, Function.Monoid {
-- error: conflicting instances for Number
-- currently in scope: Prod.Monoid
-- proposed additional instance: Sum.Monoid
append(inc)(inc)(with Sum.Monoid {empty})
}
But using different instances in different scopes would be allowed:
with Prod.Monoid, Function.Monoid {
append(inc)(inc)
}(with Sum.Monoid {empty})
Although the term you proposed would still be possible to write like this, at least it would be explicit about where abstraction boundaries lay.
Making such a feature work correctly in the presence of polymorphism and higher-order functions seems like it could be... exciting. Possibly publishable-research-level exciting. I'd be interested to find out whether a focused attempt can be pushed all the way through.
In some situations you can safely use a local explicit instance. In Haskell the reflection
library can be used to create such a local instance. See the Reified Monoids for example.
There was also a paper about building that into the language and perhaps making it more usable and general, but I have not read it: Coherent Explicit Dictionary Application for Haskell by Thomas Winant and Dominique Devriese, here is a quote from their contributions section:
In this paper, we propose a new form of explicit dictionary instantiation that preserves coherence and that is safe with respect to global uniqueness of instances, but can be directly applied to common use cases.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With