In Scala, what is the need for right hand side in case of "curry" and "compose"

I’m new to Scala and trying to understand generic aspect of it. Take “curry” as an exmaple.
In Scala, the function “curry” is defined as below

def curry[A, B, C](f: (A, B) => C): A => (B => C) = a => b => f(a, b)

I wonder what is the need for the right hand side?
Because as far as I understand, this expression “curry[A, B, C](f: (A, B) => C): A=> (B => C)” is enough to logically generate this content “a => b => f(a, b)”
I mean, in the case of creation of such a level of generic, we could have a more compact syntax without having the right hand side.

Moreover, this same question I also have for other cases like “compose” also. I wish to understand more insight about these.

Please correct me if I’m wrong. Thank you so much.

Not sure why you are splitting the equation in this way.
The left hand side should be everything on the left of the eq = sign
which is just the function signature

def curry[A, B, C](f: (A, B) => C): A => (B => C)

on the right hand you have the implementation

a => b => f(a, b)

Thank you for your feedback @qqupp .

What you mentioned, I’ve understood. The reason I ask is because: Except for the Scala’s syntax demand, from my reasoning:

  • The left hand contains sufficient logic that provide fully meaning by itself already.

  • The right hand side is just the supplement.

I mean, only by reading the left hand side expression, one person can create the right hand side, then, what is the need to create the right hand expression (of course Scala syntax require, but I’m not referring to the syntax).

it depends on the signature I believe, sometimes a function can have only one possible implementation, but sometimes other functions can have more than one implementations even if they are generic, so I guess we need to specify what implementations we want to use.

First, I’m not aware of any language that implements this kind of implementation derivation - AFAIK neither Haskell nor Idris can do this for you, for example. Second, you could always throw an exception or go into an infinite loop - not often desirable, but it’d be valid code that type-checks. So there are implementation alternatives…

1 Like

Assuming parametricity the type of curry is uniquely inhabited, so a proof search in Idris or Agda should be able to fill it in for you. But as you correctly point out, this does not hold in general in Scala. In addition to looping or crashing, you could for example look at the runtime types of the arguments and do different things depending on what you discover.

But one place where Scala does infer terms is in implicit resolution, in which it constructs terms that uniquely satisfy the requested type based on rules the programmer provides. In such cases the body of the method could simply be implicitly (or summon in Scala 3). For example, in this definition

val foo: cats.Semigroup[(Int, List[String])] = implicitly 

the body is indeed uniquely determined by its type, according to the derivation rules for Semigroup, and implicitly tells the compiler to fill it in for you.

2 Likes

Indeed, you can direct Idris to “write” the code for you in this case…

$ cat -n Main.idr
     1	curry : ((a, b) -> c) -> a -> b -> c
     2	
$ idris2 Main.idr --client ':ac! 1 curry'
$ cat -n Main.idr
     1	curry : ((a, b) -> c) -> a -> b -> c
     2	curry f x y = ?curry_rhs
     3	
$ idris2 Main.idr --client ':ps! 2 curry_rhs'
$ cat -n Main.idr
     1	curry : ((a, b) -> c) -> a -> b -> c
     2	curry f x y = f (x, y)
     3	

…but eventually, it must be present - there still is “a need for the RHS”.

2 Likes

Could you please suggest me an example that a generic function could be implemented in different variants?

Just the basic options I mentioned:

  def curry[A, B, C](f: (A, B) => C): A => B => C =
    a => b => f(a,b)

  def curryExc[A, B, C](f: (A, B) => C): A => B => C =
    throw new IllegalStateException("won't curry")

  def curryInf[A, B, C](f: (A, B) => C): A => B => C =
    curryInf(f)
1 Like