7
\$\begingroup\$

What general tips do you have for golfing in Scala 3? I'm looking for ideas that can be applied to code golf problems in general that are at least somewhat specific to Scala 3 (e.g. "remove comments" is not an answer). Please post one tip per answer.

Also see Tips for golfing in Scala

\$\endgroup\$
1
  • \$\begingroup\$ I'm willing to award a bounty if any answers this question. (100 if it's trivial or if it expands on one of the tips I've put here already, and 200 if it's really good). Bounty \$\endgroup\$
    – user
    Commented Sep 7, 2020 at 20:49

7 Answers 7

2
\$\begingroup\$

Use parameter untupling

Parameter untupling allows you to write functions accepting a single tuple as input as if they were functions accepting 2 parameters. Basically, you can replace these

list.map{case(a,b)=>a+b}
//and
list.map{case(a,b)=>Seq(a,b)}
//or
list.map(t=>t._1+t._2)
//and
list.map(t=>Seq(t._1,t._2))

with this, which is both shorter and more beautiful.

list.map(_+_)
//and
list.map(Seq(_,_))
\$\endgroup\$
2
\$\begingroup\$

Optional braces

In match expressions, at least, you can save a byte by using indentation-based syntax.

a match
case b=>c
case d=>e

is 27 bytes, while the below code is 28.

a match{case b=>c case d=>e}

As a side note, match also gets special treatment when it comes to chaining. The following Scala 2 code:

("foo"match{case x=>x})match{case x=>x}

can now be written without the parentheses:

"foo"match{case x=>x}match{case x=>x}
\$\endgroup\$
2
\$\begingroup\$

Use @main and toplevel functions

If you need to make a full program, use the @main annotation on a toplevel main method.

@main def m={code}

It's a lot shorter than these 2 approaches:

object M extends App{code}
object M{def main(a:Array[String])={code}}
\$\endgroup\$
1
\$\begingroup\$

Leave out new with universal apply methods

You can call constructors without using new (like Kotlin), treating all classes as if they have apply methods in their companions. This saves 4 bytes every time you instantiate something, since new Foo() becomes just Foo().

This also applies to givens (although you rarely need them for code golf):

given foo as Foo=new Foo() //bad
given foo as Foo=Foo()     //better
given foo as Foo           //great
\$\endgroup\$
1
\$\begingroup\$

Numeric Literals

Edit: As @stewSquared pointed out, this only works in experimental releases now

You can now write a number literal for any user-defined type with a given/implicit instance of the FromDigits trait, such as BigInt and BigDecimal.

In Scala 2, to pass the number 9,999,999,999,999,999 to a function accepting BigInts, you would have to use BigInt("9999999999999999"), but now you can just directly say 9999999999999999 without using the apply method or constructor explicitly.

You can also use underscores, scientific notation, hex, etc., just like any normal number literal, as long as the FromDigits instance for that type supports that notation, of course. Both BigInt and BigDecimal are okay with underscores, but only BigInt allows hex, and only BigDecimal allows scientific notation (with decimals).

\$\endgroup\$
4
  • 1
    \$\begingroup\$ It would be interesting (and worth mentioning) if 1e9 typechecks when used as BigInt, since scientific notation is usually treated as a float literal, not int, and typechecked as such. Hex is also a thing when used for large enough integers. \$\endgroup\$
    – Bubbler
    Commented Sep 8, 2020 at 9:08
  • \$\begingroup\$ @Bubbler Yup, fixed it. Unfortunately, they only allow the same format as ordinary Ints and Doubles (afaik) \$\endgroup\$
    – user
    Commented Sep 8, 2020 at 16:27
  • \$\begingroup\$ This doesn't seem to be true in 2022 with Scala 3.2.0. Ohhh wait it's an experimental feature that can't be used in the main release. \$\endgroup\$ Commented Sep 25, 2022 at 4:07
  • \$\begingroup\$ @stewSquared Aww, didn't notice that. When I wrote the answer, either experimental features were still available in the main release or this wasn't considered an experimental feature. I'll edit the answer to state that \$\endgroup\$
    – user
    Commented Oct 14, 2022 at 14:09
0
\$\begingroup\$

New Control Syntax

In most cases, the new control syntax doesn't shorten code (and is actually more verbose), it comes in handy in for comprehensions. If your expression or pattern ends or starts with a parentheses or double quote, you can save a byte or two:

for((x,y,z)<-Seq((1,2,3),(3,4,5));y<-"foo")yield y+x  //Old-style syntax
for(x,y,z)<-Seq((1,2,3),(3,4,5));y<-"foo"yield y+x    //New control syntax

It's not as useful as some other things, like parameter untupling, but I often find myself using for comprehensions for function calls like f(foo) or Seq(1,2), which end in parentheses.

See it run in Scastie.

\$\endgroup\$
0
\$\begingroup\$

Use match types for recursive functions that pattern match

Making a typealias with a match type is sometimes shorter than using a proper function. For example, take this implementation of the KI calculus (not an actual thing). The type doesn't need :Any, so you save 7 bytes in all (type is 1 byte more than def).

//A normal function
def r(t:Any):Any=t match{case(('K',x),y)=>r(x)case('I',x)=>r(x)case t=>t}
//With match types
type R[T]=T match{case(('K',x),y)=>R[x]case('I',x)=>R[x]case _=>T}

Admittedly, in this particular example, you could use auto-tupling, ->, pattern-matching function literals, etc. to make the def shorter, but this is just one case. When you're not dealing with tuples, or the return type is more complex than just Any, or you have multiple parameters, this tip becomes more useful.

Use type constructors for two-argument functions

When you define a type with two type parameters, you can treat it like an infix operator. Not so for methods. Suppose you have the following two definitions

def>(a:Int,b:Int)=...
type +[A,B]=...

When you invoke >, you can call it with >(a,b), whereas for +, you can call it like A+B. You can use any other operator you want if you want a specific precedence.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.