From 6446bf8c1a6940795241d3b07f1a1cc7e0b37236 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Mon, 7 Jun 2021 21:42:45 -0400 Subject: [PATCH 1/3] Update some docs --- compiler/builtins/docs/List.roc | 28 ++++++++++++++++++++++++++++ compiler/builtins/docs/Num.roc | 27 +++++++++++++++++++++++++++ compiler/builtins/docs/Str.roc | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) diff --git a/compiler/builtins/docs/List.roc b/compiler/builtins/docs/List.roc index 2f3248da4b..f130ed35bb 100644 --- a/compiler/builtins/docs/List.roc +++ b/compiler/builtins/docs/List.roc @@ -263,6 +263,34 @@ sortDesc : List elem, (elem -> Num *) -> List elem ## ## `map` functions like this are common in Roc, and they all work similarly. ## See for example #Result.map, #Set.map, and #Map.map. +## +## ## Perfomrance +## +## `map` may run either sequentially or in parallel, depending on its workload. +## +## For example, when running `map` on a very small list, with a very quick +## transformation function, running it in parallel might actually slow it down +## because the coordination overhead would exceed the savings from parallelizing +## the work. On the other hand, a very long list might benefit massively from +## parallelization, as might a medium-sized list with a transformation funtion +## that takes a long time to run. +## +## As with all behind-the-scenes optimizations, Roc's compiler uses heuristics +## to decides whether a given `map` invocation should be parallelized. If it +## decides to parallelize it, the platform is responsible for deciding how +## many threads to devote to a particular `map` call. (The `map` call will +## always return the same answer regardless of any of this.) +## +## Assigning threads is up to the platform because the platform has full +## information about the availability of system resources, including information +## that may not be available to application code (and especially to package code +## that isn't coupled to any particular platform). +## +## For example, if the platform knows that 5 out of 8 threads are currently +## busy doing other work, then it may choose to split the `map` workload across +## the 3 remaining threads. The next time you call `map`, the platform may know +## that only 2 out of 8 threads are currently busy, so it may allocate 6 threads +## to it instead. map : List before, (before -> after) -> List after ## This works like #List.map, except it also passes the index diff --git a/compiler/builtins/docs/Num.roc b/compiler/builtins/docs/Num.roc index a17a88ebca..81a6f137a5 100644 --- a/compiler/builtins/docs/Num.roc +++ b/compiler/builtins/docs/Num.roc @@ -913,8 +913,35 @@ shrWrap : Int a, Int a -> Int a ## [Endianness](https://en.wikipedia.org/wiki/Endianness) Endi : [ Big, Little ] +## The [Endi] argument does not matter for [U8] and [I8], since they have +## only one byte. toBytes : Num *, Endi -> List U8 +## If the bytes begin with a valid #U8 number, return +## that number along with the rest of the bytes after it. +parseU8 : List U8 -> Result { val : U8, rest : List U8 } [ Expected [ NumU8 ]* List U8 ]* +parseI8 : List U8 -> Result { val : I8, rest : List U8 } [ Expected [ NumI8 ]* List U8 ]* +parseU16 : List U8, Endi -> Result { val : U16, rest : List U8 } [ Expected [ NumU16 Endi ]* (List U8) ]* +parseI16 : List U8, Endi -> Result { val : I16, rest : List U8 } [ Expected [ NumI16 Endi ]* (List U8) ]* +parseU32 : List U8, Endi -> Result { val : U32, rest : List U8 } [ Expected [ NumU32 Endi ]* (List U8) ]* +parseI32 : List U8, Endi -> Result { val : I32, rest : List U8 } [ Expected [ NumI32 Endi ]* (List U8) ]* +parseU64 : List U8, Endi -> Result { val : U64, rest : List U8 } [ Expected [ NumU64 Endi ]* (List U8) ]* +parseI64 : List U8, Endi -> Result { val : I64, rest : List U8 } [ Expected [ NumI64 Endi ]* (List U8) ]* +parseU128 : List U8, Endi -> Result { val : U128, rest : List U8 } [ Expected [ NumU128 Endi ]* (List U8) ]* +parseI128 : List U8, Endi -> Result { val : I128, rest : List U8 } [ Expected [ NumI128 Endi ]* (List U8) ]* +parseF64 : List U8, Endi -> Result { val : F64, rest : List U8 } [ Expected [ NumF64 Endi ]* (List U8) ]* +parseF32 : List U8, Endi -> Result { val : F32, rest : List U8 } [ Expected [ NumF32 Endi ]* (List U8) ]* +parseDec : List U8, Endi -> Result { val : Dec, rest : List U8 } [ Expected [ NumDec Endi ]* (List U8) ]* + +## when Num.parseBytes bytes Big is +## Ok { val: f64, rest } -> ... +## Err (ExpectedNum (Float Binary64)) -> ... +parseBytes : List U8, Endi -> Result { val : Num a, rest : List U8 } [ ExpectedNum a ]* + +## when Num.fromBytes bytes Big is +## Ok f64 -> ... +## Err (ExpectedNum (Float Binary64)) -> ... +fromBytes : List U8, Endi -> Result (Num a) [ ExpectedNum a ]* ## Comparison diff --git a/compiler/builtins/docs/Str.roc b/compiler/builtins/docs/Str.roc index f811b75271..2c61b076ab 100644 --- a/compiler/builtins/docs/Str.roc +++ b/compiler/builtins/docs/Str.roc @@ -444,6 +444,23 @@ toF64 : Str -> Result U128 [ InvalidF64 ]* toF32 : Str -> Result I128 [ InvalidF32 ]* toDec : Str -> Result Dec [ InvalidDec ]* +## If the string represents a valid number, return that number. +## +## The exact number type to look for will be inferred from usage. Here's an +## example where the `Err` branch matches `Integer Signed64`, which causes this to +## parse an [I64] because [I64] is defined as `I64 : Num [ Integer [ Signed64 ] ]`. +## +## >>> when Str.toNum "12345" is +## >>> Ok i64 -> "The I64 was: \(i64)" +## >>> Err (ExpectedNum (Integer Signed64)) -> "Not a valid I64!" +## +## If the string is exactly `"NaN"`, `"∞"`, or `"-∞"`, they will be accepted +## only when converting to [F64] or [F32] numbers, and will be translated accordingly. +## +## This never accepts numbers with underscores or commas in them. For more +## advanced options, see [parseNum]. +toNum : Str -> Result (Num a) [ ExpectedNum a ]* + ## If the string begins with a valid #U8 number, return ## that number along with the rest of the string after it. parseU8 : Str, NumParseConfig -> Result { val : U8, rest : Str } [ Expected [ NumU8 ]* Str ]* @@ -467,6 +484,22 @@ parseDec : Str, NumParseConfig -> Result { val : Dec, rest : Str } [ Expected [ parseF64 : Str, NumParseConfig -> Result { val : F64, rest : Str } [ Expected [ NumF64 ]* Str ]* parseF32 : Str, NumParseConfig -> Result { val : F32, rest : Str } [ Expected [ NumF32 ]* Str ]* +## If the string begins with an [Int] or a [finite](Num.isFinite) [Frac], return +## that number along with the rest of the string after it. +## +## The exact number type to look for will be inferred from usage. Here's an +## example where the `Err` branch matches `Float Binary64`, which causes this to +## parse an [F64] because [F64] is defined as `F64 : Num [ Fraction [ Float64 ] ]`. +## +## >>> when Str.parseNum input {} is +## >>> Ok { val: f64, rest } -> "The F64 was: \(f64)" +## >>> Err (ExpectedNum (Fraction Float64)) -> "Not a valid F64!" +## +## If the string begins with `"NaN"`, `"∞"`, and `"-∞"` (which do not represent +## [finite](Num.isFinite) numbers), they will be accepted only when parsing +## [F64] or [F32] numbers, and translated accordingly. +parseNum : Str, NumParseConfig -> Result { val : Num a, rest : Str } [ ExpectedNum a ]* + ## Notes: ## * You can allow a decimal mark for integers; they'll only parse if the numbers after it are all 0. ## * For `wholeSep`, `Required` has a payload for how many digits (e.g. "required every 3 digits") From 043091b1e629ff8373c4241e6cec53acccbcb9fd Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 8 Jun 2021 21:38:22 -0400 Subject: [PATCH 2/3] Revert List.map idea Not convinced this is a good design. Might be, might not be, but needs further investigation. --- compiler/builtins/docs/List.roc | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/compiler/builtins/docs/List.roc b/compiler/builtins/docs/List.roc index f130ed35bb..2f3248da4b 100644 --- a/compiler/builtins/docs/List.roc +++ b/compiler/builtins/docs/List.roc @@ -263,34 +263,6 @@ sortDesc : List elem, (elem -> Num *) -> List elem ## ## `map` functions like this are common in Roc, and they all work similarly. ## See for example #Result.map, #Set.map, and #Map.map. -## -## ## Perfomrance -## -## `map` may run either sequentially or in parallel, depending on its workload. -## -## For example, when running `map` on a very small list, with a very quick -## transformation function, running it in parallel might actually slow it down -## because the coordination overhead would exceed the savings from parallelizing -## the work. On the other hand, a very long list might benefit massively from -## parallelization, as might a medium-sized list with a transformation funtion -## that takes a long time to run. -## -## As with all behind-the-scenes optimizations, Roc's compiler uses heuristics -## to decides whether a given `map` invocation should be parallelized. If it -## decides to parallelize it, the platform is responsible for deciding how -## many threads to devote to a particular `map` call. (The `map` call will -## always return the same answer regardless of any of this.) -## -## Assigning threads is up to the platform because the platform has full -## information about the availability of system resources, including information -## that may not be available to application code (and especially to package code -## that isn't coupled to any particular platform). -## -## For example, if the platform knows that 5 out of 8 threads are currently -## busy doing other work, then it may choose to split the `map` workload across -## the 3 remaining threads. The next time you call `map`, the platform may know -## that only 2 out of 8 threads are currently busy, so it may allocate 6 threads -## to it instead. map : List before, (before -> after) -> List after ## This works like #List.map, except it also passes the index From bec4884028d6b3225660ebcd31e89df5b06bdae6 Mon Sep 17 00:00:00 2001 From: Richard Feldman Date: Tue, 8 Jun 2021 21:57:22 -0400 Subject: [PATCH 3/3] Try having only one parse function --- compiler/builtins/docs/Num.roc | 16 ---------------- compiler/builtins/docs/Str.roc | 23 ----------------------- 2 files changed, 39 deletions(-) diff --git a/compiler/builtins/docs/Num.roc b/compiler/builtins/docs/Num.roc index 81a6f137a5..db8d41bd7f 100644 --- a/compiler/builtins/docs/Num.roc +++ b/compiler/builtins/docs/Num.roc @@ -917,22 +917,6 @@ Endi : [ Big, Little ] ## only one byte. toBytes : Num *, Endi -> List U8 -## If the bytes begin with a valid #U8 number, return -## that number along with the rest of the bytes after it. -parseU8 : List U8 -> Result { val : U8, rest : List U8 } [ Expected [ NumU8 ]* List U8 ]* -parseI8 : List U8 -> Result { val : I8, rest : List U8 } [ Expected [ NumI8 ]* List U8 ]* -parseU16 : List U8, Endi -> Result { val : U16, rest : List U8 } [ Expected [ NumU16 Endi ]* (List U8) ]* -parseI16 : List U8, Endi -> Result { val : I16, rest : List U8 } [ Expected [ NumI16 Endi ]* (List U8) ]* -parseU32 : List U8, Endi -> Result { val : U32, rest : List U8 } [ Expected [ NumU32 Endi ]* (List U8) ]* -parseI32 : List U8, Endi -> Result { val : I32, rest : List U8 } [ Expected [ NumI32 Endi ]* (List U8) ]* -parseU64 : List U8, Endi -> Result { val : U64, rest : List U8 } [ Expected [ NumU64 Endi ]* (List U8) ]* -parseI64 : List U8, Endi -> Result { val : I64, rest : List U8 } [ Expected [ NumI64 Endi ]* (List U8) ]* -parseU128 : List U8, Endi -> Result { val : U128, rest : List U8 } [ Expected [ NumU128 Endi ]* (List U8) ]* -parseI128 : List U8, Endi -> Result { val : I128, rest : List U8 } [ Expected [ NumI128 Endi ]* (List U8) ]* -parseF64 : List U8, Endi -> Result { val : F64, rest : List U8 } [ Expected [ NumF64 Endi ]* (List U8) ]* -parseF32 : List U8, Endi -> Result { val : F32, rest : List U8 } [ Expected [ NumF32 Endi ]* (List U8) ]* -parseDec : List U8, Endi -> Result { val : Dec, rest : List U8 } [ Expected [ NumDec Endi ]* (List U8) ]* - ## when Num.parseBytes bytes Big is ## Ok { val: f64, rest } -> ... ## Err (ExpectedNum (Float Binary64)) -> ... diff --git a/compiler/builtins/docs/Str.roc b/compiler/builtins/docs/Str.roc index 2c61b076ab..3a5ed0c88e 100644 --- a/compiler/builtins/docs/Str.roc +++ b/compiler/builtins/docs/Str.roc @@ -461,29 +461,6 @@ toDec : Str -> Result Dec [ InvalidDec ]* ## advanced options, see [parseNum]. toNum : Str -> Result (Num a) [ ExpectedNum a ]* -## If the string begins with a valid #U8 number, return -## that number along with the rest of the string after it. -parseU8 : Str, NumParseConfig -> Result { val : U8, rest : Str } [ Expected [ NumU8 ]* Str ]* -parseI8 : Str, NumParseConfig -> Result { val : I8, rest : Str } [ Expected [ NumI8 ]* Str ]* -parseU16 : Str, NumParseConfig -> Result { val : U16, rest : Str } [ Expected [ NumU16 ]* Str ]* -parseI16 : Str, NumParseConfig -> Result { val : I16, rest : Str } [ Expected [ NumI16 ]* Str ]* -parseU32 : Str, NumParseConfig -> Result { val : U32, rest : Str } [ Expected [ NumU32 ]* Str ]* -parseI32 : Str, NumParseConfig -> Result { val : I32, rest : Str } [ Expected [ NumI32 ]* Str ]* -parseU64 : Str, NumParseConfig -> Result { val : U64, rest : Str } [ Expected [ NumU64 ]* Str ]* -parseI64 : Str, NumParseConfig -> Result { val : I64, rest : Str } [ Expected [ NumI64 ]* Str ]* -parseU128 : Str, NumParseConfig -> Result { val : U128, rest : Str } [ Expected [ NumU128 ]* Str ]* -parseI128 : Str, NumParseConfig -> Result { val : I128, rest : Str } [ Expected [ NumI128 ]* Str ]* -parseDec : Str, NumParseConfig -> Result { val : Dec, rest : Str } [ Expected [ NumDec ]* Str ]* - -## If the string begins with a [finite](Num.isFinite) [F64] number, return -## that number along with the rest of the string after it. -## -## If the string begins with `"NaN"`, `"∞"`, and `"-∞"` (which do not represent -## [finite](Num.isFinite) numbers), they will be similarly accepted and -## translated into their respective [F64] values. -parseF64 : Str, NumParseConfig -> Result { val : F64, rest : Str } [ Expected [ NumF64 ]* Str ]* -parseF32 : Str, NumParseConfig -> Result { val : F32, rest : Str } [ Expected [ NumF32 ]* Str ]* - ## If the string begins with an [Int] or a [finite](Num.isFinite) [Frac], return ## that number along with the rest of the string after it. ##