What is the type alias rules in Scala? - scala

So, just can't understand why does this compiles?
type <=[B, A] = A => B
type F[A] = Double <= A //why our alias <= is allowed here?
What is the syntax rule of forming type aliases which allows constructions like these?
Can we free play only with the order => here like in this case?

There is a very simple rule that every type A B C is the same as B[A, C]. Evidently this only works for types with 2 type parameters.

I could not find the definition of =>
Yeah, it is embedded in the compiler implementation / language specification.
But, as you just show, it is quite easy to reimplement in on userland; it would look like this:
type =>[+A, +B] = Function1[A, B]
how Scala compiler parse an alias expression like sequence of types Type1 Type2 Type3
The language specs say that it supports infix types, so things like: A OP B are equivalent to OP[A, B]

Related

Scala 3 type lambdas. Example for "curried type parameters"

Scala 3 has a powerful mechanism of expressing type constructors via type lambdas.
Even simple type lambdas can do powerful things like expressing partial application of a type constructor (see for ex https://stackoverflow.com/a/75428709/336184 ).
Docs mention "Curried Type Parameters" like
type TL = [X] =>> [Y] =>> (X, Y)
this looks like even more abstract thing.
Question:
Can anyone give a working example with an implementation of such a type lambda? Also - what is a practical purpose of such an abstraction? Any parallels in Haskell?
type TL = [X] =>> [Y] =>> ...
is the same as
type TL[X] = [Y] =>> ...
and should be the same as
type TL[X][Y] = ...
if there were multiple type-parameter lists (MTPL).
So [X] =>> [Y] =>> ... should be a way to introduce such type anonymously.
MTPL along with named type parameters could be useful for specifying some type parameters and inferring the rest of them.
Cannot prove equivalence with a path dependent type
Curried type "does not take type parameters"
https://contributors.scala-lang.org/t/multiple-type-parameter-lists-in-dotty-si-4719/2399
https://github.com/scala/bug/issues/4719
https://docs.scala-lang.org/scala3/reference/experimental/named-typeargs.html
For example specifying some type parameters and inferring the rest can be necessary for type-level calculations. Currently for type-level calculations people either make type parameters nested or use type members instead of type parameters.
When are dependent types needed in Shapeless?
In Haskell you can write
foo :: forall (f :: * -> * -> *) . ()
foo = ()
but in Scala without MTPL implemented, currently you can't write
def foo[F[_][_]]: Unit = ()
you can only write
def foo[F[_,_]]: Unit = ()
If there were MTPL then for a definition like def foo[F[_][_]]... it would be convenient having curried type lambdas [X] =>> [Y] =>> ..., you could use them at a call site foo[[X] =>> [Y] =>> ...].
In Haskell all type constructors are curried and there are no type lambdas
Lambda for type expressions in Haskell?

how to normalize a `scala.reflect.api.Types.Type`

How to implement the function normalize(type: Type): Type such that:
A =:= B if and only if normalize(A) == normalize(B) and normalize(A).hashCode == normalize(B).hashCode.
In other words, normalize must return equal results for all equivalent Type instances; and not equal nor equivalent results for all pair of non equivalent inputs.
There is a deprecated method called normalize in the TypeApi, but it does not the same.
In my particular case I only need to normalize types that represent a class or a trait (tpe.typeSymbol.isClass == true).
Edit 1: The fist comment suggests that such a function might not be possible to implement in general. But perhaps it is possible if we add another constraint:
B is obtained by navigating from A.
In the next example fooType would be A, and nextAppliedType would be B:
import scala.reflect.runtime.universe._
sealed trait Foo[V]
case class FooImpl[V](next: Foo[V]) extends Foo[V]
scala> val fooType = typeOf[Foo[Int]]
val fooType: reflect.runtime.universe.Type = Foo[Int]
scala> val nextType = fooType.typeSymbol.asClass.knownDirectSubclasses.iterator.next().asClass.primaryConstructor.typeSignature.paramLists(0)(0).typeSignature
val nextType: reflect.runtime.universe.Type = Foo[V]
scala> val nextAppliedType = appliedType(nextType.typeConstructor, fooType.typeArgs)
val nextAppliedType: reflect.runtime.universe.Type = Foo[Int]
scala> println(fooType =:= nextAppliedType)
true
scala> println(fooType == nextAppliedType)
false
Inspecting the Type instances with showRaw shows why they are not equal (at least when Foo and FooImpl are members of an object, in this example, the jsfacile.test.RecursionTest object):
scala> showRaw(fooType)
val res2: String = TypeRef(SingleType(SingleType(SingleType(ThisType(<root>), jsfacile), jsfacile.test), jsfacile.test.RecursionTest), jsfacile.test.RecursionTest.Foo, List(TypeRef(ThisType(scala), scala.Int, List())))
scala> showRaw(nextAppliedType)
val res3: String = TypeRef(ThisType(jsfacile.test.RecursionTest), jsfacile.test.RecursionTest.Foo, List(TypeRef(ThisType(scala), scala.Int, List())))
The reason I need this is difficult to explain. Let's try:
I am developing this JSON library which works fine except when there is a recursive type reference. For example:
sealed trait Foo[V]
case class FooImpl[V](next: Foo[V]) extends Foo[V]
That happens because the parser/appender it uses to parse and format are type classes that are materialized by an implicit macro. And when an implicit parameter is recursive the compiler complains with a divergence error.
I tried to solve that using by-name implicit parameter but it not only didn't solve the recursion problem, but also makes many non recursive algebraic data type to fail.
So, now I am trying to solve this problem by storing the resolved materializations in a map, which also would improve the compilation speed. And that map key is of type Type. So I need to normalize the Type instances, not only to be usable as key of a map, but also to equalize the values generated from them.
If I understood you well, any equivalence class would be fine. There is no preference.
I suspect you didn't. At least "any equivalence class would be fine", "There is no preference" do not sound good. I'll try to elaborate.
In math there is such construction as factorization. If you have a set A and equivalence relation ~ on this set (relation means that for any pair of elements from A we know whether they are related a1 ~ a2 or not, equivalence means symmetricity a1 ~ a2 => a2 ~ a1, reflexivity a ~ a, transitivity a1 ~ a2, a2 ~ a3 => a1 ~ a3) then you can consider the factor-set A/~ whose elements are all equivalence classes A/~ = { [a] | a ∈ A} (the equivalence class
[a] = {b ∈ A | b ~ a}
of an element a is a set consisting of all elements equivalent (i.e. ~-related) to a).
The axiom of choice says that there is a map (function) from A/~ to A i.e. we can select a representative in every equivalence class and in such way form a subset of A (this is true if we accept the axiom of choice, if we don't then it's not clear whether we get a set in such way). But even if we accept the axiom of choice and therefore there is a function A/~ -> A this doesn't mean we can construct such function.
Simple example. Let's consider the set of all real numbers R and the following equivalence relation: two real numbers are equivalent r1 ~ r2 if their difference is a rational number
r2 - r1 = p/q ∈ Q
(p, q≠0 are arbitrary integers). This is an equivalence relation. So it's known that there is a function selecting a single real number from any equivalence class but how to define this function explicitly for a specific input? For example what is the output of this function for the input being the equivalence class of 0 or 1 or π or e or √2 or log 2...?
Similarly, =:= is an equivalence relation on types, so it's known that there is a function normalize (maybe there are even many such functions) selecting a representative in every equivalence class but how to prefer a specific one (how to define or construct the output explicitly for any specific input)?
Regarding your struggle against implicit divergence. It's not necessary that you've selected the best possible approach. Sounds like you're doing some compiler work manually. How do other json libraries solve the issue? For example Circe? Besides by-name implicits => there is also shapeless.Lazy / shapeless.Strict (not equivalent to by-name implicits). If you have specific question about deriving type classes, overcoming implicit divergence maybe you should start a different question about that?
Regarding your approach with HashMap with Type keys. I'm still reminding that we're not supposed to rely on == for Types, correct comparison is =:=. So you should build your HashMap using =:= rather than ==. Search at SO for something like: hashmap custom equals.
Actually I guess your normalize sounds like you want some caching. You should have a type cache. Then if you asked to calculate normalize(typ) you should check whether in the cache there is already a t such that t =:= typ. If so you should return t, otherwise you should add typ to the cache and return typ.
This satisfies your requirement: A =:= B if and only if normalize(A) == normalize(B) (normalize(A).hashCode == normalize(B).hashCode should follow from normalize(A) == normalize(B)).
Regarding transformation of fooType into nextAppliedType try
def normalize(typ: Type): Type = typ match {
case TypeRef(pre, sym, args) =>
internal.typeRef(internal.thisType(pre.typeSymbol), sym, args)
}
Then normalize(fooType) == nextAppliedType should be true.

Scala currying example in the tutorial is confusing me

I'm reading a bit about Scala currying here and I don't understand this example very much:
def foldLeft[B](z: B)(op: (B, A) => B): B
What is the [B] in square brackets? Why is it in brackets? The B after the colon is the return type right? What is the type?
It looks like this method has 2 parameter lists: one with a parameter named z and one with a parameter named op which is a function.
op looks like it takes a function (B, A) => B). What does the right side mean? It returns B?
And this is apparently how it is used:
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val res = numbers.foldLeft(0)((m, n) => m + n)
print(res) // 55
What is going on? Why wasn't the [B] needed when called?
In Scala documentation that A type (sometimes A1) is often the placeholder for a collection's element type. So if you have...
List('c','q','y').foldLeft( //....etc.
...then A becomes the reference for Char because the list is a List[Char].
The B is a placeholder for a 2nd type that foldLeft will have to deal with. Specifically it is the type of the 1st parameter as well as the type of the foldLeft result. Most of the time you actually don't need to specify it when foldLeft is invoked because the compiler will infer it. So, yeah, it could have been...
numbers.foldLeft[Int](0)((m, n) => m + n)
...but why bother? The compiler knows it's an Int and so does anyone reading it (anyone who knows Scala).
(B, A) => B is a function that takes 2 parameters, one of type B and one of type A, and produces a result of type B.
What is the [B] in square brackets?
A type parameter (that's also known as "generics" if you've seen something like Java before)
Why is it in brackets?
Because type parameters are written in brackets, that's just Scala syntax. Java, C#, Rust, C++ use angle brackets < > for similar purposes, but since arrays in Scala are accessed as arr(idx), and (unlike Haskell or Python) Scala does not use [ ... ] for list comprehensions, the square brackets could be used for type parameters, and there was no need for angular brackets (those are more difficult to parse anyway, especially in a language which allows almost arbitrary names for infix and postfix operators).
The B after the colon is the return type right?
Right.
What is the type?
Ditto. The return type is B.
It looks like this method has 2 parameter lists: one with a parameter named z and one with a parameter named op which is a function.
This method has a type parameter B and two argument lists for value arguments, correct. This is done to simplify the type inference: the type B can be inferred from the first argument z, so it does not have to be repeated when writing down the lambda-expression for op. This wouldn't work if z and op were in the same argument list.
op looks like it takes a function (B, A) => B.
The type of the argument op is (B, A) => B, that is, Function2[B, A, B], a function that takes a B and an A and returns a B.
What does the right side mean? It returns B?
Yes.
What is going on?
m acts as accumulator, n is the current element of the list. The fold starts with integer value 0, and then accumulates from left to right, adding up all numbers. Instead of (m, n) => m + n, you could have written _ + _.
Why wasn't the [B] needed when called?
It was inferred from the type of z. There are many other cases where the generic type cannot be inferred automatically, then you would have to specify the return type explicitly by passing it as an argument in the square brackets.
This is what is called polymorphism. The function can work on multiple types and you sometimes want to give a parameter of what type will be worked with. Basically the B is a type parameter and can either be given explicitly as a type, which would be Int and then it should be given in square brackets or implicitly in parentheses like you did with the 0. Read about polymorphism here Scala polymorphism

Scala Function.tupled and Function.untupled equivalent for variable arity, or, calling variable arity function with tuple

I was trying to do some stuff last night around accepting and calling a generic function (i.e. the type is known at the call site, but potentially varies across call sites, so the definition should be generic across arities).
For example, suppose I have a function f: (A, B, C, ...) => Z. (There are actually many such fs, which I do not know in advance, and so I cannot fix the types nor count of A, B, C, ..., Z.)
I'm trying to achieve the following.
How do I call f generically with an instance of (A, B, C, ...)? If the signature of f were known in advance, then I could do something involving Function.tupled f or equivalent.
How do I define another function or method (for example, some object's apply method) with the same signature as f? That is to say, how do I define a g for which g(a, b, c, ...) type checks if and only if f(a, b, c, ...) type checks? I was looking into Shapeless's HList for this. From what I can tell so far, HList at least solves the "representing an arbitrary arity args list" issue, and also, Shapeless would solve the conversion to and from tuple issue. However, I'm still not sure I understand how this would fit in with a function of generic arity, if at all.
How do I define another function or method with a related type signature to f? The biggest example that comes to mind now is some h: (A, B, C, ...) => SomeErrorThing[Z] \/ Z.
I remember watching a conference presentation on Shapeless some time ago. While the presenter did not explicitly demonstrate these things, what they did demonstrate (various techniques around abstracting/genericizing tuples vs HLists) would lead me to believe that similar things as the above are possible with the same tools.
Thanks in advance!
Yes, Shapeless can absolutely help you here. Suppose for example that we want to take a function of arbitrary arity and turn it into a function of the same arity but with the return type wrapped in Option (I think this will hit all three points of your question).
To keep things simple I'll just say the Option is always Some. This takes a pretty dense four lines:
import shapeless._, ops.function._
def wrap[F, I <: HList, O](f: F)(implicit
ftp: FnToProduct.Aux[F, I => O],
ffp: FnFromProduct[I => Option[O]]
): ffp.Out = ffp(i => Some(ftp(f)(i)))
We can show that it works:
scala> wrap((i: Int) => i + 1)
res0: Int => Option[Int] = <function1>
scala> wrap((i: Int, s: String, t: String) => (s * i) + t)
res1: (Int, String, String) => Option[String] = <function3>
scala> res1(3, "foo", "bar")
res2: Option[String] = Some(foofoofoobar)
Note the appropriate static return types. Now for how it works:
The FnToProduct type class provides evidence that some type F is a FunctionN (for some N) that can be converted into a function from some HList to the original output type. The HList function (a Function1, to be precise) is the Out type member of the instance, or the second type parameter of the FnToProduct.Aux helper.
FnFromProduct does the reverse—it's evidence that some F is a Function1 from an HList to some output type that can be converted into a function of some arity to that output type.
In our wrap method, we use FnToProduct.Aux to constrain the Out of the FnToProduct instance for F in such a way that we can refer to the HList parameter list and the O result type in the type of our FnFromProduct instance. The implementation is then pretty straightforward—we just apply the instances in the appropriate places.
This may all seem very complicated, but once you've worked with this kind of generic programming in Scala for a while it becomes more or less intuitive, and we'd of course be happy to answer more specific questions about your use case.

Scala infix type aliasing for >2 type parameters?

I know in Scala, you can do type ===>[A, B] = Map[A, B] and then you can use infix notation to define def foo: String ===> Int which is same as saying def foo: Map[String, Int]. Is there any way to exploit this infix notation to create types with >2 arguments? For example, I want something like this:
type A ~> B ~~~> C to be an alias of say Map[A, Pair[B, C]] ?
Is there anyway I can write something line this:
type A to B -> C as alias for (A, B, C) type?
Interestingly operator precedence as defined for symbolic methods doesn't seem to hold for symbolic type aliases. Instead infix type aliases are always evaluated left associative:
type -[A,B] = Map[A,B]
type /[A,B] = Map[A,B] // '/' has higher precedence than '-' as an operator
classOf[A - B / C] // Class[/[-[A,B],C]]
classOf[A / B - C] // Class[-[/[A,B],C]]
Unfortunately that means it will never be possible to do what you ask for without parentheses like this:
classOf[A - (B / C)] // Class[-[A,/[B,C]]
So the closest answer is the following:
type ~>[A,B] = Map[A,B]
type ~~~>[A,B] = Pair[A,B]
classOf[A ~> (B ~~~> C)] // Map[A,Pair[B,C]]
Ommitting the parentheses will only be possible if you use right associative aliases (ending with :)
type ~:[A,B] = Map[A,B]
type ~~~:[A,B] = Pair[A,B]
classOf[A ~: B ~~~: C] // Map[A,Pair[B,C]]
Again, unfortunately since all type aliases have the same precedence it is not possible to mix right and left associative aliases without parentheses.
Concerning the second part of your question: (A,B,C) is syntactic sugar for Tuple3[A,B,C] which is a type with three parameters. As infix types only take two parameters, I'm afraid that I believe there is no way to represent this type solely with infix types. You would always end up with nested two parameter types (e.g. (A,(B,C)) or ((A,B),C).
Short answer: no. A ~> B ~> C cannot mean Map[A, Pair[B, C]].
It could be made to mean Map[A, Map[B, C]], though, or Pair[A, Pair[B, C]].