Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Number System #298

Merged
merged 14 commits into from
Sep 15, 2020
Merged

Number System #298

merged 14 commits into from
Sep 15, 2020

Conversation

peblair
Copy link
Member

@peblair peblair commented Sep 10, 2020

This PR adds the first version of our long-awaited number system! 🤓 📈

Number Types

Grain has the following number types:

  • Int32: 32-bit integers
  • Int64: 64-bit integers
  • Float32: 32-bit (single-precision) IEEE floats
  • Float64: 64-bit (double-precision) IEEE floats
  • Rationals: Exact representations of integral fractions (e.g. 1/3)

Syntax

The syntax works as you would probably expect:

# for integers
100
# for floats
3.14
1e4

A nice side effect of this PR is that literal integers in Grain now can run the full range of possible Int64 values!

Note that there is no syntax for rationals; instead, they are constructed by dividing two integer-like numbers.
By default, all number literals are of type Number. That way, they can all be freely combined using integer operations:

 λ grain git:(number-system) > cat hello.gr
print(1 + 1)
print(1.5 + 2)
print((1 / 3) + 0.33333)
 λ grain git:(number-system) > grain hello.gr
2
3.5
0.6666633333333334

It even supports equality checks across number types!

 λ grain git:(number-system) > cat hello.gr
print(1.0 == 1)
 λ grain git:(number-system) > grain hello.gr
true

Do be aware that this means that runtime errors are possible! (sorry, @ospencer)

 λ grain git:(number-system) > cat hello.gr
print(1.6 % 5)
 λ grain git:(number-system) > grain hello.gr
o [GrainError]: Cannot coerce to integer: 1.6
    at s (/Users/pblair/views/grain/runtime/dist/grain-runtime.js:1:24339)
    at wasm-function[56]:0x3486
    at wasm-function[57]:0x3499
    at wasm-function[84]:0x1d12
    at wasm-function[41]:0x868
    at t.start (/Users/pblair/views/grain/runtime/dist/grain-runtime.js:1:46860)
    at a.start (/Users/pblair/views/grain/runtime/dist/grain-runtime.js:1:25783)
    at m.runFile (/Users/pblair/views/grain/runtime/dist/grain-runtime.js:1:5069)
    at async run (/Users/pblair/views/grain/cli/bin/run.js:14:7) {
  code: 24
}

What if you want to work with a specific subtype of number? We have syntax for that:

# int32
1l
# int64
1L
# float32
1.0l
# float64
1.0L

For example, this will not type-check:

1L + 1

Unfortunately, neither will this!

1L + 1L

For the moment, all of the builtin operations need to be performed on Numbers. As such, we have a variety of utilities to convert back and forth between the specific subtypes and Number:

coerceInt64ToNumber
coerceInt32ToNumber
coerceFloat64ToNumber
coerceFloat32ToNumber
coerceRationalToNumber
coerceNumberToInt64
coerceNumberToInt32
coerceNumberToRational
coerceNumberToFloat64
coerceNumberToFloat32

Known Issues

  • We currently store rationals as a pair of i32 numbers. This should probably be upgraded to i64 in order to avoid weird edge cases in the future
  • The above list of conversion functions is a bit unwieldy. We would like to add special syntax for these conversions in the future.

Closes #250 and closes #255 .

@peblair peblair requested a review from a team September 10, 2020 15:59
@ospencer
Copy link
Member

Woohoo! Happy to see this here. Haven't gotten to really dig into the code yet, but re: the coerce functions, I believe we wanted them to exist in the respective libraries:

# int32.gr
export let fromNumber ...
export let toNumber ...

and then the same for int64.gr, float{32, 64}.gr, etc.

@phated
Copy link
Member

phated commented Sep 10, 2020

@ospencer I'm pretty sure we want them in runtime so we can add our sugar without adding additional deps.

@ospencer
Copy link
Member

@phated sorry, my code example was kinda bad. They shouldn't be implemented in the separate libraries, just imported from the runtime there. When we want to add syntax, we can import the same functions from the runtime into pervasives.

Here's really what the code should look like:

# int64.gr
import foreign wasm coerceInt64ToNumber : Int64 -> Number as toNumber from 'stdlib-external/runtime'
import foreign wasm coerceNumberToInt64 : Int64 -> Number as fromNumber from 'stdlib-external/runtime'

And then once we're ready, we can do this in Pervasives (the syntax is just a fake example):

# pervasives.gr
import foreign wasm coerceInt64ToNumber : Int64 -> Number as (@@) from 'stdlib-external/runtime'

Copy link
Member

@ospencer ospencer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well done! Code looks really good. I left some notes for some places we can do some cleanup, as well as some places we should create some issues. Sorry about the number of comments, but when it's time to merge it's a lot easier for me to scroll through and see that the comments are resolved rather than reading through the whole thing again. 😛

@belph could you also update the data_representations document with the layout of the new numbers?

compiler/src/codegen/compcore.re Outdated Show resolved Hide resolved
compiler/src/codegen/compcore.re Outdated Show resolved Hide resolved
compiler/src/codegen/compcore.re Outdated Show resolved Hide resolved
compiler/src/codegen/compcore.re Outdated Show resolved Hide resolved
compiler/src/codegen/compcore.re Outdated Show resolved Hide resolved
stdlib/stdlib-external/numbers.ts Outdated Show resolved Hide resolved
stdlib/stdlib-external/numbers.ts Outdated Show resolved Hide resolved
stdlib/stdlib-external/numbers.ts Outdated Show resolved Hide resolved
stdlib/stdlib-external/numbers.ts Outdated Show resolved Hide resolved
stdlib/stdlib-external/numbers.ts Outdated Show resolved Hide resolved
compiler/src/parsing/parser.dyp Outdated Show resolved Hide resolved

The downside of this tag for numbers is the loss of one bit of information. There are 2^31 possible values for signed numbers in Grain rather than 2^32.
The downside of this tag for numbers is the loss of one bit of information. If larger numbers are needed, then users must use one of the other (heap-allocated) number types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sorta implies that the user has to do something rather than the numbers being automatically promoted. Am I mistaken?

runtime/src/utils/utils.js Show resolved Hide resolved
stdlib/stdlib-external/numbers.ts Outdated Show resolved Hide resolved
stdlib/stdlib-external/runtime.ts Outdated Show resolved Hide resolved
Copy link
Member

@ospencer ospencer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work! Feels really sweet to see this come to fruition.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Lang: Floats Compiler: Number system
3 participants