Migrating to 1.0¶
Onion is cleaning up its syntax and APIs before 1.0, while breaking a few programs is still cheap. Each breaking change is listed here with how to update your code.
After 1.0, Onion keeps a compatibility promise: a program that compiles on 1.x keeps compiling and means the same thing on later 1.x releases.
ret is now yield¶
A do block's final value is written with yield.
// Before
val sum = do[Option] { a <- getA(); b <- getB(); ret a + b }
// Now
val sum = do[Option] { a <- getA(); b <- getB(); yield a + b }
- What changed:
yieldis a reserved word;retis an ordinary identifier again. - What you see: old code reports
local variable ret is not found., with a hint to writeyield. - A Java method named
yield: call it with backticks, e.g.Thread::`yield`(). - Update a whole project:
Check the result if you use
retas a variable name.
2147483648 needs a - directly in front¶
The literal 2147483648 (and 9223372036854775808L) is valid only as the whole operand of a
unary -, as in -2147483648. Anywhere else it used to wrap silently to the minimum value.
// Before: compiled, with a surprising value
val a = 1 - 2147483648 // -2147483647, by wraparound
val b = -2147483648 as Long // 2147483648: `as` binds tighter than `-`
val c = -2147483648 / 2 // 1073741824: `/` binds tighter than `-`
// Now
val a = 1 - 2147483648L // or 1L - 2147483648L
val b = -2147483648L // or (-2147483648) as Long
val c = (-2147483648) / 2 // -1073741824
- What changed: the magnitude of the minimum value is checked where it is negated, not at the token.
- What you see:
integer literal out of range for Int (-2147483648..2147483647) -- add an L suffix to make it a Long literal., at the literal (... for Long ...with theLsuffix). - Still fine:
-2147483648,x + -2147483648,3 * -2147483648,-9223372036854775808L, and hex or binary bit patterns such as0x80000000.
A decimal B/S literal must fit the signed range¶
A decimal Byte literal now takes -128..127 and a decimal Short literal -32768..32767,
as in Java. Larger values used to wrap silently.
// Before: compiled, with a surprising value
val mask: Byte = 255B // -1
val port: Short = 40000S // -25536
val n = -128B as Int // 128: `as` binds tighter than `-`
// Now
val mask: Byte = 0b11111111B // -1, written as a bit pattern
val port: Int = 40000 // a Short cannot hold 40000
val n = (-128B) as Int // -128
- What changed: like
2147483648, the magnitudes128Band32768Sare valid only as the whole operand of a-. - What you see:
integer literal out of range for Byte (-128..127, or a bit pattern up to 0b11111111) -- ..., at the literal (... for Short ...with theSsuffix). - Still fine: a binary bit pattern such as
0b11111111B, or a hex one with theSsuffix such as0xFFFFS. - Hex and
B: in a hex literalBis a digit, never a suffix.0xABis the Int 171 and0xFFBis the Int 4091. For a Byte, write0b11111111Bor-1B. - Where such a literal no longer fits a Byte (
val x: Byte = 0xFFB,f(0xFFB)with a Byte parameter), the error carries a hint that says so. - Where it still fits, nothing is reported and the value changes:
bs[0] = 0x1Bstores 27 (was 1), andval y = 0x7FBis 2043 (was the Byte 127). Search your code for hex literals ending inB(0x...B).
A quoted String in a config file is read without its quotes¶
A shape name = config record now reads a String value written in double quotes as quoted
text. This lets a value keep leading or trailing spaces through print and parse.
# Before: the quotes were part of the value
name = "Alice" # read as "Alice" (7 characters)
# Now
name = "Alice" # read as Alice (5 characters)
name = " Alice " # read as Alice (spaces kept)
name = Alice # read as Alice (unquoted values are still trimmed)
name = "\"Alice\"" # read as "Alice" (to keep literal quotes)
- What changed: inside the quotes,
\"is a quote and\\is a backslash. Any other backslash is kept as it is, so"C:\temp"reads asC:\temp. - What you see: a file that already relied on literal quotes still parses, so there is no
error or hint; the value just loses its outer quotes. Search your config files for values
that start with
". A value with no closing", or with a bare"inside it ("a" and "b"), is now aDefectnaming the line. - Only
Stringcomponents: anInt,Booleanor other scalar is read exactly as before.
An empty [] or [:] takes the type of its siblings¶
In a list literal, or in the values of a map literal, an empty [] or [:] now has the
type the other elements agree on. Before, it widened the whole literal to Object.
val adj = [[1, 2], []]
// Before: List[Object], so adj[1].add(5) was E0005, and this compiled
adj.add("header")
// Now: List[List[Int]], so adj[1].add(5) compiles, and this is E0005
adj.add("header")
// To keep the old type, annotate it
val xs: List[Object] = [[1, 2], []]
- What changed: the empty literal is typed after its siblings, from their type. Elements are still evaluated in source order.
- What you see:
E0005namingList[List[Int]].add(String)(orMap[String, List[Int]].set(...)) where the code mixed element kinds, or passed the value to aList[Object]parameter. - Still fine: an explicit annotation wins, and a literal with no typed sibling (
[[], []]) is typed as before.