In Scala, is there a shorthand for reducing a generic type's arity?
Asked Answered
B

3

11

I want to call Scalaz's pure method to put a value into the State monad. The following works:

type IntState[A] = State[Int, A]
val a = "a".pure[IntState]
a(1)
    (Int, java.lang.String) = (1,a)

I can also eliminate the type alias (thanks Scalaz's Pure.scala):

val a = "a".pure[({type T[A]=State[Int,A]})#T]
a(1)
    (Int, java.lang.String) = (1,a)

But that is extremely clunky. Is there a shorter way to synthesize a type like this? Like placeholder syntax for function literals, is there something like:

"a".pure[State[Int, *]]
Bucovina answered 4/10, 2011 at 5:0 Comment(1)
I don't think Scala has such syntactic sugar, since michid cited partial type application as an example of the utility of type projections.Lutes
T
6

For concise partial type application (arity-2) in Scala, you can infix type notation as followings.

type ![F[_, _], X] = TF { type ![Y] = F[X,  Y] }

"a".pure[(State!Int)# !]

Note that we can infix notation for two arity type constructor (or type alias).

Tridentum answered 4/10, 2011 at 14:29 Comment(2)
I'd rename the second ! to ?, so I could write (State!Int)#?.Retroact
Non-operator name would be preferable for the type member so you can skip the space after the type projection, ie: trait ![F[_, _], A] { type X[B] = F[A, B] }; foo[(State!Int)#X]. Don't think it can get much more compact than that.Micropyle
C
6

Not sure if this qualifies as better, but here is one approach that @kmizu tweeted the other day:

scala> trait TF {
     |   type Apply[A]
     | }
defined trait TF

scala> type Curried2[F[_, _]] = TF {
     |   type Apply[X] = TF {
     |     type Apply[Y] = F[X, Y]
     |   }
     | }
defined type alias Curried2

scala> "a".pure[Curried2[State]#Apply[Int]#Apply]
res7: scalaz.State[Int,java.lang.String] = scalaz.States$$anon$1@1dc1d18

You can make it look a little nicer by using symbolic type aliases.

scala> type ![F[_, _]] = TF {
     |   type ![X] = TF {
     |     type ![Y] = F[X, Y]
     |   }
     | }
defined type alias $bang

scala> "a".pure[![State]# ![Int]# !]
res9: scalaz.State[Int,java.lang.String] = scalaz.States$$anon$1@1740235
Crest answered 4/10, 2011 at 5:52 Comment(3)
What happens when you want to bind the first type parameter? I guess you need another version of Curried2?Postfix
@IttayD: That won't be called currying, and you'd be better off using the type lambda syntax in that case. (This small advantage of type lambda syntax over Haskell's curried type constructor syntax was also noted by Tony Morris on some mailing list.) The syntax I have suggested in this answer is for emulating Haskell style partial type application. ![State]# ! is equivalent to State in Haskell, ![State]# ![Int]# ! to State Int etc.Crest
@JesperNordenberg, I don't think that's a good reason to downvote.Crest
T
6

For concise partial type application (arity-2) in Scala, you can infix type notation as followings.

type ![F[_, _], X] = TF { type ![Y] = F[X,  Y] }

"a".pure[(State!Int)# !]

Note that we can infix notation for two arity type constructor (or type alias).

Tridentum answered 4/10, 2011 at 14:29 Comment(2)
I'd rename the second ! to ?, so I could write (State!Int)#?.Retroact
Non-operator name would be preferable for the type member so you can skip the space after the type projection, ie: trait ![F[_, _], A] { type X[B] = F[A, B] }; foo[(State!Int)#X]. Don't think it can get much more compact than that.Micropyle
D
1

the most popular way of reducing arity is kind-projector (https://github.com/non/kind-projector) plugin that is also used in cats library. By enabling this plugin your example can be transformed to:

val a = "a".pure[State[Int, ?]]

Note: this syntax will be enabled in Dotty by default.

Douro answered 20/9, 2017 at 19:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.