mirror of
https://github.com/roc-lang/roc.git
synced 2024-09-20 15:27:45 +03:00
Merge remote-tracking branch 'origin/trunk' into infer-borrow
This commit is contained in:
commit
0e5283efd2
@ -299,21 +299,20 @@ pub fn gen(src: &[u8], target: Triple, opt_level: OptLevel) -> Result<(String, S
|
||||
panic!("A specialization was still marked InProgress after monomorphization had completed: {:?} with layout {:?}", symbol, layout);
|
||||
}
|
||||
Done(proc) => {
|
||||
let (fn_val, arg_basic_types) =
|
||||
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
|
||||
headers.push((proc, fn_val, arg_basic_types));
|
||||
headers.push((proc, fn_val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build each proc using its header info.
|
||||
for (proc, fn_val, arg_basic_types) in headers {
|
||||
for (proc, fn_val) in headers {
|
||||
// NOTE: This is here to be uncommented in case verification fails.
|
||||
// (This approach means we don't have to defensively clone name here.)
|
||||
//
|
||||
// println!("\n\nBuilding and then verifying function {}\n\n", name);
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val);
|
||||
|
||||
if fn_val.verify(true) {
|
||||
fpm.run_on(&fn_val);
|
||||
|
@ -250,19 +250,18 @@ pub fn gen(
|
||||
// We have to do this in a separate pass first,
|
||||
// because their bodies may reference each other.
|
||||
for ((symbol, layout), proc) in procs.get_specialized_procs(arena) {
|
||||
let (fn_val, arg_basic_types) =
|
||||
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
|
||||
headers.push((proc, fn_val, arg_basic_types));
|
||||
headers.push((proc, fn_val));
|
||||
}
|
||||
|
||||
// Build each proc using its header info.
|
||||
for (proc, fn_val, arg_basic_types) in headers {
|
||||
for (proc, fn_val) in headers {
|
||||
// NOTE: This is here to be uncommented in case verification fails.
|
||||
// (This approach means we don't have to defensively clone name here.)
|
||||
//
|
||||
// println!("\n\nBuilding and then verifying function {:?}\n\n", proc);
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val);
|
||||
|
||||
if fn_val.verify(true) {
|
||||
fpm.run_on(&fn_val);
|
||||
|
@ -347,8 +347,17 @@ min : List (Num a) -> Result (Num a) [ ListWasEmpty ]*
|
||||
##
|
||||
## If the given index is outside the bounds of the list, returns the original
|
||||
## list unmodified.
|
||||
##
|
||||
## To drop the element at a given index, instead of replacing it, see #List.drop.
|
||||
put : List elem, Len, elem -> List elem
|
||||
|
||||
## Drops the element at the given index from the list.
|
||||
##
|
||||
## This has no effect if the given index is outside the bounds of the list.
|
||||
##
|
||||
## To replace the element at a given index, instead of dropping it, see #List.put.
|
||||
drop : List elem, Len -> List elem
|
||||
|
||||
## Adds a new element to the end of the list.
|
||||
##
|
||||
## >>> List.append [ "a", "b" ] "c"
|
||||
|
@ -14,7 +14,8 @@ len : Set * -> Len
|
||||
|
||||
add : Set 'elem, 'elem -> Set 'elem
|
||||
|
||||
rem : Set 'elem, 'elem -> Set 'elem
|
||||
## Drops the given element from the set.
|
||||
drop : Set 'elem, 'elem -> Set 'elem
|
||||
|
||||
## Convert each element in the set to something new, by calling a conversion
|
||||
## function on each of them. Then return a new set of the converted values.
|
||||
|
@ -7,33 +7,36 @@ interface Str exposes [ Str, isEmpty, join ] imports []
|
||||
##
|
||||
## _For more advanced use cases like working with raw [code points](https://unicode.org/glossary/#code_point),
|
||||
## see the [roc/unicode](roc/unicode) package. For locale-specific text
|
||||
## functions (including capitalizing a string, as capitalization rules vary by locale)
|
||||
## see the [roc/locale](roc/locale) package._
|
||||
## functions (including uppercasing strings, as capitalization rules vary by locale;
|
||||
## in English, `"i"` capitalizes to `"I"`, but [in Turkish](https://en.wikipedia.org/wiki/Dotted_and_dotless_I#In_computing),
|
||||
## the same `"i"` capitalizes to `"İ"` - as well as sorting strings, which also varies
|
||||
## by locale; `"ö"` is sorted differently in German and Swedish) see the [roc/locale](roc/locale) package._
|
||||
##
|
||||
## ### Unicode
|
||||
##
|
||||
## Unicode can represent text values which span multiple languages, symbols, and emoji.
|
||||
## Here are some valid Roc strings:
|
||||
##
|
||||
## "Roc"
|
||||
## "Roc!"
|
||||
## "鹏"
|
||||
## "🕊"
|
||||
##
|
||||
## Every Unicode string is a sequence of [grapheme clusters](https://unicode.org/glossary/#grapheme_cluster).
|
||||
## A grapheme cluster corresponds to what a person reading a string might call a "character",
|
||||
## but because the term "character" is used to mean many different concepts across
|
||||
## different programming languages, the documentation for Roc strings intentionally
|
||||
## avoids it. Instead, we use the term "clusters" as a shorthand for "grapheme clusters."
|
||||
## Every Unicode string is a sequence of [extended grapheme clusters](http://www.unicode.org/glossary/#extended_grapheme_cluster).
|
||||
## An extended grapheme cluster represents what a person reading a string might
|
||||
## call a "character" - like "A" or "ö" or "👩👩👦👦".
|
||||
## Because the term "character" means different things in different areas of
|
||||
## programming, and "extended grapheme cluster" is a mouthful, in Roc we use the
|
||||
## term "grapheme" as a shorthand for the more precise "extended grapheme cluster."
|
||||
##
|
||||
## You can get the number of grapheme clusters in a string by calling #Str.countClusters on it:
|
||||
## You can get the number of graphemes in a string by calling #Str.countGraphemes on it:
|
||||
##
|
||||
## Str.countClusters "Roc!"
|
||||
## Str.countClusters "折り紙"
|
||||
## Str.countClusters "🕊"
|
||||
## Str.countGraphemes "Roc!"
|
||||
## Str.countGraphemes "折り紙"
|
||||
## Str.countGraphemes "🕊"
|
||||
##
|
||||
## > The `countClusters` function walks through the entire string to get its answer,
|
||||
## > The `countGraphemes` function walks through the entire string to get its answer,
|
||||
## > so if you want to check whether a string is empty, you'll get much better performance
|
||||
## > by calling `Str.isEmpty myStr` instead of `Str.countClusters myStr == 0`.
|
||||
## > by calling `Str.isEmpty myStr` instead of `Str.countGraphemes myStr == 0`.
|
||||
##
|
||||
## ### Escape sequences
|
||||
##
|
||||
@ -107,7 +110,7 @@ Str : [ @Str ]
|
||||
##
|
||||
## If you want to keep all the digits, passing the same float to #Str.num
|
||||
## will do that.
|
||||
decimal : Float *, Ulen -> Str
|
||||
decimal : Float *, Len -> Str
|
||||
|
||||
## Split a string around a separator.
|
||||
##
|
||||
@ -118,7 +121,7 @@ decimal : Float *, Ulen -> Str
|
||||
##
|
||||
## >>> Str.split "1,2,3" ""
|
||||
##
|
||||
## To split a string into its individual grapheme clusters, use #Str.clusters
|
||||
## To split a string into its individual graphemes, use #Str.graphemes
|
||||
split : Str, Str -> List Str
|
||||
|
||||
## Check
|
||||
@ -136,9 +139,9 @@ endsWith : Str, Str -> Bool
|
||||
|
||||
contains : Str, Str -> Bool
|
||||
|
||||
anyClusters : Str, (Str -> Bool) -> Bool
|
||||
anyGraphemes : Str, (Str -> Bool) -> Bool
|
||||
|
||||
allClusters : Str, (Str -> Bool) -> Bool
|
||||
allGraphemes : Str, (Str -> Bool) -> Bool
|
||||
|
||||
## Combine
|
||||
|
||||
@ -154,60 +157,69 @@ join : List Str -> Str
|
||||
joinWith : List Str, Str -> Str
|
||||
|
||||
## Add to the start of a string until it has at least the given number of
|
||||
## grapheme clusters.
|
||||
## graphemes.
|
||||
##
|
||||
## >>> Str.padClustersStart "0" 5 "36"
|
||||
## >>> Str.padGraphemesStart "0" 5 "36"
|
||||
##
|
||||
## >>> Str.padClustersStart "0" 1 "36"
|
||||
## >>> Str.padGraphemesStart "0" 1 "36"
|
||||
##
|
||||
## >>> Str.padClustersStart "0" 5 "12345"
|
||||
## >>> Str.padGraphemesStart "0" 5 "12345"
|
||||
##
|
||||
## >>> Str.padClustersStart "✈️"" 5 "👩👩👦👦👩👩👦👦👩👩👦👦"
|
||||
padClustersStart : Str, Int, Str -> Str
|
||||
## >>> Str.padGraphemesStart "✈️"" 5 "👩👩👦👦👩👩👦👦👩👩👦👦"
|
||||
padGraphemesStart : Str, Int, Str -> Str
|
||||
|
||||
## Add to the end of a string until it has at least the given number of
|
||||
## grapheme clusters.
|
||||
## graphemes.
|
||||
##
|
||||
## >>> Str.padClustersStart "0" 5 "36"
|
||||
## >>> Str.padGraphemesStart "0" 5 "36"
|
||||
##
|
||||
## >>> Str.padClustersStart "0" 1 "36"
|
||||
## >>> Str.padGraphemesStart "0" 1 "36"
|
||||
##
|
||||
## >>> Str.padClustersStart "0" 5 "12345"
|
||||
## >>> Str.padGraphemesStart "0" 5 "12345"
|
||||
##
|
||||
## >>> Str.padClustersStart "✈️"" 5 "👩👩👦👦👩👩👦👦👩👩👦👦"
|
||||
padClustersEnd : Str, Int, Str -> Str
|
||||
## >>> Str.padGraphemesStart "✈️"" 5 "👩👩👦👦👩👩👦👦👩👩👦👦"
|
||||
padGraphemesEnd : Str, Int, Str -> Str
|
||||
|
||||
## Grapheme Clusters
|
||||
## Graphemes
|
||||
|
||||
## Split a string into its individual grapheme clusters.
|
||||
## Split a string into its individual graphemes.
|
||||
##
|
||||
## >>> Str.clusters "1,2,3"
|
||||
## >>> Str.graphemes "1,2,3"
|
||||
##
|
||||
## >>> Str.clusters "👍👍👍"
|
||||
## >>> Str.graphemes "👍👍👍"
|
||||
##
|
||||
clusters : Str -> List Str
|
||||
graphemes : Str -> List Str
|
||||
|
||||
## Str.countClusters "Roc!" # 4
|
||||
## Str.countClusters "七巧板" # 3
|
||||
## Str.countClusters "🕊" # 1
|
||||
## Str.countGraphemes "Roc!" # 4
|
||||
## Str.countGraphemes "七巧板" # 3
|
||||
## Str.countGraphemes "🕊" # 1
|
||||
|
||||
## Reverse the order of the string's individual grapheme clusters.
|
||||
## Reverse the order of the string's individual graphemes.
|
||||
##
|
||||
## >>> Str.reverseClusters "1-2-3"
|
||||
## >>> Str.reverseGraphemes "1-2-3"
|
||||
##
|
||||
## >>> Str.reverseClusters "🐦✈️"👩👩👦👦"
|
||||
reverseClusters : Str -> Str
|
||||
## >>> Str.reverseGraphemes "🐦✈️"👩👩👦👦"
|
||||
##
|
||||
## >>> Str.reversegraphemes "Crème Brûlée"
|
||||
reverseGraphemes : Str -> Str
|
||||
|
||||
foldClusters : Str, { start: state, step: (state, Str -> state) } -> state
|
||||
|
||||
## Returns #True if the string begins with a capital letter, and #False otherwise.
|
||||
## Returns #True if the two strings are equal when ignoring case.
|
||||
##
|
||||
## >>> Str.isCapitalized "hi"
|
||||
## >>> Str.caseInsensitiveEq "hi" "Hi"
|
||||
isCaseInsensitiveEq : Str, Str -> Bool
|
||||
|
||||
isCaseInsensitiveNeq : Str, Str -> Bool
|
||||
|
||||
walkGraphemes : Str, { start: state, step: (state, Str -> state) } -> state
|
||||
|
||||
## Returns #True if the string begins with an uppercase letter.
|
||||
##
|
||||
## >>> Str.isCapitalized "Hi"
|
||||
##
|
||||
## >>> Str.isCapitalized " Hi"
|
||||
##
|
||||
## >>> Str.isCapitalized "hi"
|
||||
##
|
||||
## >>> Str.isCapitalized "Česká"
|
||||
##
|
||||
## >>> Str.isCapitalized "Э"
|
||||
@ -218,18 +230,63 @@ foldClusters : Str, { start: state, step: (state, Str -> state) } -> state
|
||||
##
|
||||
## >>> Str.isCapitalized ""
|
||||
##
|
||||
## Since the rules for how to capitalize an uncapitalized string vary by locale,
|
||||
## see the [roc/locale](roc/locale) package for functions which do that.
|
||||
## Since the rules for how to capitalize a string vary by locale,
|
||||
## (for example, in English, `"i"` capitalizes to `"I"`, but
|
||||
## [in Turkish](https://en.wikipedia.org/wiki/Dotted_and_dotless_I#In_computing),
|
||||
## the same `"i"` capitalizes to `"İ"`) see the [roc/locale](roc/locale) package
|
||||
## package for functions which capitalize strings.
|
||||
isCapitalized : Str -> Bool
|
||||
|
||||
## ## Code Units
|
||||
## Returns #True if the string consists entirely of uppercase letters.
|
||||
##
|
||||
## Besides grapheme clusters, another way to break down strings is into
|
||||
## >>> Str.isAllUppercase "hi"
|
||||
##
|
||||
## >>> Str.isAllUppercase "Hi"
|
||||
##
|
||||
## >>> Str.isAllUppercase "HI"
|
||||
##
|
||||
## >>> Str.isAllUppercase " Hi"
|
||||
##
|
||||
## >>> Str.isAllUppercase "Česká"
|
||||
##
|
||||
## >>> Str.isAllUppercase "Э"
|
||||
##
|
||||
## >>> Str.isAllUppercase "東京"
|
||||
##
|
||||
## >>> Str.isAllUppercase "🐦"
|
||||
##
|
||||
## >>> Str.isAllUppercase ""
|
||||
isAllUppercase : Str -> Bool
|
||||
|
||||
## Returns #True if the string consists entirely of lowercase letters.
|
||||
##
|
||||
## >>> Str.isAllLowercase "hi"
|
||||
##
|
||||
## >>> Str.isAllLowercase "Hi"
|
||||
##
|
||||
## >>> Str.isAllLowercase "HI"
|
||||
##
|
||||
## >>> Str.isAllLowercase " Hi"
|
||||
##
|
||||
## >>> Str.isAllLowercase "Česká"
|
||||
##
|
||||
## >>> Str.isAllLowercase "Э"
|
||||
##
|
||||
## >>> Str.isAllLowercase "東京"
|
||||
##
|
||||
## >>> Str.isAllLowercase "🐦"
|
||||
##
|
||||
## >>> Str.isAllLowercase ""
|
||||
isAllLowercase : Str -> Bool
|
||||
|
||||
## Code Units
|
||||
|
||||
## Besides graphemes, another way to break down strings is into
|
||||
## raw code unit integers.
|
||||
##
|
||||
## Code units are no substitute for grapheme clusters!
|
||||
## Code units are no substitute for graphemes!
|
||||
## These functions exist to support advanced use cases like those found in
|
||||
## [roc/unicode](roc/unicode), and using code units when grapheme clusters would
|
||||
## [roc/unicode](roc/unicode), and using code units when graphemes would
|
||||
## be more appropriate can very easily lead to bugs.
|
||||
##
|
||||
## For example, `Str.countGraphemes "👩👩👦👦"` returns `1`,
|
||||
@ -239,7 +296,7 @@ isCapitalized : Str -> Bool
|
||||
|
||||
## Return a #List of the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit).
|
||||
## (To split the string into a #List of smaller #Str values instead of #U8 values,
|
||||
## see #Str.split and #Str.clusters.)
|
||||
## see #Str.split and #Str.graphemes.)
|
||||
##
|
||||
## >>> Str.toUtf8 "👩👩👦👦"
|
||||
##
|
||||
@ -250,12 +307,12 @@ isCapitalized : Str -> Bool
|
||||
## >>> Str.toUtf8 "🐦"
|
||||
##
|
||||
## For a more flexible function that walks through each of these #U8 code units
|
||||
## without creating a #List, see #Str.foldUtf8 and #Str.foldRevUtf8.
|
||||
## without creating a #List, see #Str.walkUtf8 and #Str.walkRevUtf8.
|
||||
toUtf8 : Str -> List U8
|
||||
|
||||
## Return a #List of the string's #U16 UTF-16 [code units](https://unicode.org/glossary/#code_unit).
|
||||
## (To split the string into a #List of smaller #Str values instead of #U16 values,
|
||||
## see #Str.split and #Str.clusters.)
|
||||
## see #Str.split and #Str.graphemes.)
|
||||
##
|
||||
## >>> Str.toUtf16 "👩👩👦👦"
|
||||
##
|
||||
@ -266,12 +323,12 @@ toUtf8 : Str -> List U8
|
||||
## >>> Str.toUtf16 "🐦"
|
||||
##
|
||||
## For a more flexible function that walks through each of these #U16 code units
|
||||
## without creating a #List, see #Str.foldUtf16 and #Str.foldRevUtf16.
|
||||
## without creating a #List, see #Str.walkUtf16 and #Str.walkRevUtf16.
|
||||
toUtf16 : Str -> List U16
|
||||
|
||||
## Return a #List of the string's #U32 UTF-32 [code units](https://unicode.org/glossary/#code_unit).
|
||||
## (To split the string into a #List of smaller #Str values instead of #U32 values,
|
||||
## see #Str.split and #Str.clusters.)
|
||||
## see #Str.split and #Str.graphemes.)
|
||||
##
|
||||
## >>> Str.toUtf32 "👩👩👦👦"
|
||||
##
|
||||
@ -282,13 +339,12 @@ toUtf16 : Str -> List U16
|
||||
## >>> Str.toUtf32 "🐦"
|
||||
##
|
||||
## For a more flexible function that walks through each of these #U32 code units
|
||||
## without creating a #List, see #Str.foldUtf32 and #Str.foldRevUtf32.
|
||||
## without creating a #List, see #Str.walkUtf32 and #Str.walkRevUtf32.
|
||||
toUtf32 : Str -> List U32
|
||||
|
||||
|
||||
## Walk through the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit)
|
||||
## to build up a state.
|
||||
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.foldClusters.)
|
||||
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.walkGraphemes.)
|
||||
##
|
||||
## Here are the #U8 values that will be passed to `step` when this function is
|
||||
## called on various strings:
|
||||
@ -299,11 +355,11 @@ toUtf32 : Str -> List U32
|
||||
## * `"🐦"` passes 240, 159, 144, 166
|
||||
##
|
||||
## To convert a #Str into a plain `List U8` of UTF-8 code units, see #Str.toUtf8.
|
||||
foldUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||
walkUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||
|
||||
## Walk through the string's #U16 UTF-16 [code units](https://unicode.org/glossary/#code_unit)
|
||||
## to build up a state.
|
||||
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.foldClusters.)
|
||||
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.walkGraphemes.)
|
||||
##
|
||||
## Here are the #U16 values that will be passed to `step` when this function is
|
||||
## called on various strings:
|
||||
@ -314,11 +370,11 @@ foldUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||
## * `"🐦"` passes 55357, 56358
|
||||
##
|
||||
## To convert a #Str into a plain `List U16` of UTF-16 code units, see #Str.toUtf16.
|
||||
foldUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||
walkUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||
|
||||
## Walk through the string's #U32 UTF-32 [code units](https://unicode.org/glossary/#code_unit)
|
||||
## to build up a state.
|
||||
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.foldClusters.)
|
||||
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.walkGraphemes.)
|
||||
##
|
||||
## Here are the #U32 values that will be passed to `step` when this function is
|
||||
## called on various strings:
|
||||
@ -329,12 +385,12 @@ foldUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||
## * `"🐦"` passes 128038
|
||||
##
|
||||
## To convert a #Str into a plain `List U32` of UTF-32 code units, see #Str.toUtf32.
|
||||
foldUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
|
||||
walkUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
|
||||
|
||||
|
||||
## Walk backwards through the string's #U8 UTF-8 [code units](https://unicode.org/glossary/#code_unit)
|
||||
## to build up a state.
|
||||
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.foldClusters.)
|
||||
## (If you want a `step` function which receives a #Str instead of an #U8, see #Str.walkGraphemes.)
|
||||
##
|
||||
## Here are the #U8 values that will be passed to `step` when this function is
|
||||
## called on various strings:
|
||||
@ -345,11 +401,11 @@ foldUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
|
||||
## * `"🐦"` passes 166, 144, 159, 240
|
||||
##
|
||||
## To convert a #Str into a plain `List U8` of UTF-8 code units, see #Str.toUtf8.
|
||||
foldRevUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||
walkRevUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||
|
||||
## Walk backwards through the string's #U16 UTF-16 [code units](https://unicode.org/glossary/#code_unit)
|
||||
## to build up a state.
|
||||
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.foldClusters.)
|
||||
## (If you want a `step` function which receives a #Str instead of an #U16, see #Str.walkGraphemes.)
|
||||
##
|
||||
## Here are the #U16 values that will be passed to `step` when this function is
|
||||
## called on various strings:
|
||||
@ -360,11 +416,11 @@ foldRevUtf8 : Str, { start: state, step: (state, U8 -> state) } -> state
|
||||
## * `"🐦"` passes 56358, 55357
|
||||
##
|
||||
## To convert a #Str into a plain `List U16` of UTF-16 code units, see #Str.toUtf16.
|
||||
foldRevUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||
walkRevUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||
|
||||
## Walk backwards through the string's #U32 UTF-32 [code units](https://unicode.org/glossary/#code_unit)
|
||||
## to build up a state.
|
||||
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.foldClusters.)
|
||||
## (If you want a `step` function which receives a #Str instead of an #U32, see #Str.walkGraphemes.)
|
||||
##
|
||||
## Here are the #U32 values that will be passed to `step` when this function is
|
||||
## called on various strings:
|
||||
@ -375,4 +431,4 @@ foldRevUtf16 : Str, { start: state, step: (state, U16 -> state) } -> state
|
||||
## * `"🐦"` passes 128038
|
||||
##
|
||||
## To convert a #Str into a plain `List U32` of UTF-32 code units, see #Str.toUtf32.
|
||||
foldRevUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
|
||||
walkRevUtf32 : Str, { start: state, step: (state, U32 -> state) } -> state
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::layout_id::LayoutIds;
|
||||
use crate::llvm::build_list::{
|
||||
allocate_list, empty_polymorphic_list, list_append, list_concat, list_get_unsafe, list_join,
|
||||
list_len, list_prepend, list_repeat, list_reverse, list_set, list_single,
|
||||
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_get_unsafe,
|
||||
list_join, list_len, list_prepend, list_repeat, list_reverse, list_set, list_single,
|
||||
};
|
||||
use crate::llvm::compare::{build_eq, build_neq};
|
||||
use crate::llvm::convert::{basic_type_from_layout, collection, get_fn_type, ptr_int};
|
||||
@ -15,7 +15,7 @@ use inkwell::module::{Linkage, Module};
|
||||
use inkwell::passes::{PassManager, PassManagerBuilder};
|
||||
use inkwell::types::{BasicTypeEnum, FunctionType, IntType, StructType};
|
||||
use inkwell::values::BasicValueEnum::{self, *};
|
||||
use inkwell::values::{FloatValue, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::values::{BasicValue, FloatValue, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::AddressSpace;
|
||||
use inkwell::{IntPredicate, OptimizationLevel};
|
||||
use roc_collections::all::{ImMap, MutSet};
|
||||
@ -221,41 +221,68 @@ pub fn build_exp_literal<'a, 'ctx, 'env>(
|
||||
Byte(b) => env.context.i8_type().const_int(*b as u64, false).into(),
|
||||
Str(str_literal) => {
|
||||
if str_literal.is_empty() {
|
||||
panic!("TODO build an empty string in LLVM");
|
||||
empty_list(env)
|
||||
} else {
|
||||
let ctx = env.context;
|
||||
let builder = env.builder;
|
||||
let str_len = str_literal.len() + 1/* TODO drop the +1 when we have structs and this is no longer a NUL-terminated CString.*/;
|
||||
|
||||
let byte_type = ctx.i8_type();
|
||||
let nul_terminator = byte_type.const_zero();
|
||||
let len_val = ctx.i64_type().const_int(str_len as u64, false);
|
||||
let ptr = env
|
||||
.builder
|
||||
.build_array_malloc(ctx.i8_type(), len_val, "str_ptr")
|
||||
.unwrap();
|
||||
let len_u64 = str_literal.len() as u64;
|
||||
let elem_layout = Layout::Builtin(Builtin::Int8);
|
||||
|
||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
||||
let elem_bytes = elem_layout.stack_size(env.ptr_bytes) as u64;
|
||||
|
||||
// Copy the bytes from the string literal into the array
|
||||
for (index, byte) in str_literal.bytes().enumerate() {
|
||||
let ptr = {
|
||||
let bytes_len = elem_bytes * len_u64;
|
||||
let len_type = env.ptr_int();
|
||||
let len = len_type.const_int(bytes_len, false);
|
||||
|
||||
allocate_list(env, &elem_layout, len)
|
||||
|
||||
// TODO check if malloc returned null; if so, runtime error for OOM!
|
||||
};
|
||||
|
||||
// Copy the elements from the list literal into the array
|
||||
for (index, char) in str_literal.as_bytes().iter().enumerate() {
|
||||
let val = env
|
||||
.context
|
||||
.i8_type()
|
||||
.const_int(*char as u64, false)
|
||||
.as_basic_value_enum();
|
||||
let index_val = ctx.i64_type().const_int(index as u64, false);
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "byte") };
|
||||
unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "index") };
|
||||
|
||||
builder.build_store(elem_ptr, byte_type.const_int(byte as u64, false));
|
||||
builder.build_store(elem_ptr, val);
|
||||
}
|
||||
|
||||
// Add a NUL terminator at the end.
|
||||
// TODO: Instead of NUL-terminating, return a struct
|
||||
// with the pointer and also the length and capacity.
|
||||
let index_val = ctx.i64_type().const_int(str_len as u64 - 1, false);
|
||||
let elem_ptr =
|
||||
unsafe { builder.build_in_bounds_gep(ptr, &[index_val], "nul_terminator") };
|
||||
let ptr_bytes = env.ptr_bytes;
|
||||
let int_type = ptr_int(ctx, ptr_bytes);
|
||||
let ptr_as_int = builder.build_ptr_to_int(ptr, int_type, "list_cast_ptr");
|
||||
let struct_type = collection(ctx, ptr_bytes);
|
||||
let len = BasicValueEnum::IntValue(env.ptr_int().const_int(len_u64, false));
|
||||
let mut struct_val;
|
||||
|
||||
builder.build_store(elem_ptr, nul_terminator);
|
||||
// Store the pointer
|
||||
struct_val = builder
|
||||
.build_insert_value(
|
||||
struct_type.get_undef(),
|
||||
ptr_as_int,
|
||||
Builtin::WRAPPER_PTR,
|
||||
"insert_ptr",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
BasicValueEnum::PointerValue(ptr)
|
||||
// Store the length
|
||||
struct_val = builder
|
||||
.build_insert_value(struct_val, len, Builtin::WRAPPER_LEN, "insert_len")
|
||||
.unwrap();
|
||||
|
||||
// Bitcast to an array of raw bytes
|
||||
builder.build_bitcast(
|
||||
struct_val.into_struct_value(),
|
||||
collection(ctx, ptr_bytes),
|
||||
"cast_collection",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1416,7 +1443,7 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
|
||||
symbol: Symbol,
|
||||
layout: &Layout<'a>,
|
||||
proc: &roc_mono::ir::Proc<'a>,
|
||||
) -> (FunctionValue<'ctx>, Vec<'a, BasicTypeEnum<'ctx>>) {
|
||||
) -> FunctionValue<'ctx> {
|
||||
let args = proc.args;
|
||||
let arena = env.arena;
|
||||
let context = &env.context;
|
||||
@ -1450,15 +1477,14 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
|
||||
fn_val.set_call_conventions(FAST_CALL_CONV);
|
||||
}
|
||||
|
||||
(fn_val, arg_basic_types)
|
||||
fn_val
|
||||
}
|
||||
|
||||
pub fn build_proc<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
env: &'a Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
proc: roc_mono::ir::Proc<'a>,
|
||||
fn_val: FunctionValue<'ctx>,
|
||||
arg_basic_types: Vec<'a, BasicTypeEnum<'ctx>>,
|
||||
) {
|
||||
let args = proc.args;
|
||||
let context = &env.context;
|
||||
@ -1472,13 +1498,15 @@ pub fn build_proc<'a, 'ctx, 'env>(
|
||||
let mut scope = Scope::default();
|
||||
|
||||
// Add args to scope
|
||||
for ((arg_val, arg_type), (layout, arg_symbol)) in
|
||||
fn_val.get_param_iter().zip(arg_basic_types).zip(args)
|
||||
{
|
||||
for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) {
|
||||
set_name(arg_val, arg_symbol.ident_string(&env.interns));
|
||||
|
||||
let alloca =
|
||||
create_entry_block_alloca(env, fn_val, arg_type, arg_symbol.ident_string(&env.interns));
|
||||
let alloca = create_entry_block_alloca(
|
||||
env,
|
||||
fn_val,
|
||||
arg_val.get_type(),
|
||||
arg_symbol.ident_string(&env.interns),
|
||||
);
|
||||
|
||||
builder.build_store(alloca, arg_val);
|
||||
|
||||
|
@ -151,13 +151,9 @@ pub fn basic_type_from_layout<'ctx>(
|
||||
Float64 => context.f64_type().as_basic_type_enum(),
|
||||
Float32 => context.f32_type().as_basic_type_enum(),
|
||||
Float16 => context.f16_type().as_basic_type_enum(),
|
||||
Str | EmptyStr => context
|
||||
.i8_type()
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.as_basic_type_enum(),
|
||||
Map(_, _) | EmptyMap => panic!("TODO layout_to_basic_type for Builtin::Map"),
|
||||
Set(_) | EmptySet => panic!("TODO layout_to_basic_type for Builtin::Set"),
|
||||
List(_, _) => collection(context, ptr_bytes).into(),
|
||||
List(_, _) | Str | EmptyStr => collection(context, ptr_bytes).into(),
|
||||
EmptyList => BasicTypeEnum::StructType(collection(context, ptr_bytes)),
|
||||
},
|
||||
}
|
||||
|
@ -13,17 +13,11 @@ mod helpers;
|
||||
|
||||
#[cfg(test)]
|
||||
mod gen_primitives {
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::os::raw::c_char;
|
||||
|
||||
#[test]
|
||||
fn basic_str() {
|
||||
assert_evals_to!(
|
||||
"\"shirt and hat\"",
|
||||
CString::new("shirt and hat").unwrap().as_c_str(),
|
||||
*const c_char,
|
||||
CStr::from_ptr
|
||||
);
|
||||
assert_evals_to!("\"\"", "", &'static str);
|
||||
assert_evals_to!("\"shirt and hat\"", "shirt and hat", &'static str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -139,15 +139,14 @@ pub fn helper_without_uniqueness<'a>(
|
||||
// We have to do this in a separate pass first,
|
||||
// because their bodies may reference each other.
|
||||
for ((symbol, layout), proc) in procs.drain() {
|
||||
let (fn_val, arg_basic_types) =
|
||||
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
|
||||
headers.push((proc, fn_val, arg_basic_types));
|
||||
headers.push((proc, fn_val));
|
||||
}
|
||||
|
||||
// Build each proc using its header info.
|
||||
for (proc, fn_val, arg_basic_types) in headers {
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
|
||||
for (proc, fn_val) in headers {
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val);
|
||||
|
||||
if fn_val.verify(true) {
|
||||
function_pass.run_on(&fn_val);
|
||||
@ -333,15 +332,14 @@ pub fn helper_with_uniqueness<'a>(
|
||||
// We have to do this in a separate pass first,
|
||||
// because their bodies may reference each other.
|
||||
for ((symbol, layout), proc) in procs.drain() {
|
||||
let (fn_val, arg_basic_types) =
|
||||
build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
let fn_val = build_proc_header(&env, &mut layout_ids, symbol, &layout, &proc);
|
||||
|
||||
headers.push((proc, fn_val, arg_basic_types));
|
||||
headers.push((proc, fn_val));
|
||||
}
|
||||
|
||||
// Build each proc using its header info.
|
||||
for (proc, fn_val, arg_basic_types) in headers {
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val, arg_basic_types);
|
||||
for (proc, fn_val) in headers {
|
||||
build_proc(&env, &mut layout_ids, proc, fn_val);
|
||||
|
||||
if fn_val.verify(true) {
|
||||
fpm.run_on(&fn_val);
|
||||
|
@ -122,50 +122,6 @@ You can rewrite the above like so:
|
||||
\UserId id ->
|
||||
```
|
||||
|
||||
## Function equality
|
||||
|
||||
In Elm, if you write `(\val -> val) == (\val -> val)`, you currently get a runtime exception
|
||||
which links to [the `==` docs](https://package.elm-lang.org/packages/elm/core/latest/Basics#==), which explain why this is the current behavior and what the better version will look like.
|
||||
|
||||
> OCaml also has the "runtime exception if you compare functions for structural equality" behavior, but unlike Elm, in OCaml this appears to be the long-term design.
|
||||
|
||||
In Roc, the design is for function equality to be a compile error, but not tracked visibly
|
||||
in the type system. In this way it's like tail calls; the compiler tracks them, but
|
||||
it tracks them in a way that is not exposed to the author directly.
|
||||
|
||||
So if you write `(\val -> val) == (\val -> val)` in Roc, you'll get a compile error about "function equality," but not a type mismatch - because the types will be identical (`a -> a` in both cases).
|
||||
|
||||
The reason for this design is that in practice, the Elm stopgap runtime exception design has
|
||||
revealed that this edge case comes up absurdly infrequently - to the point where many seasoned Elm programmers do not even know this is a runtime exception, because they've never even accidentally written code that triggers it!
|
||||
|
||||
Clearly since this is detectable at compile time, it's a nicer design to give the feedback
|
||||
at compile time rather than crashing at runtime. Also, it happens so infrequently that it would
|
||||
be hard to justify lots of type signatures gaining an `Eq` constraint of some sort -
|
||||
especially when the syntax for that would be added to the language *solely* for the sake
|
||||
of this extreme edge case.
|
||||
|
||||
Instead the design is to track this behind the scenes only, and give
|
||||
a separate class of compiler error for it than the "type mismatch" error you'd see
|
||||
for this in Rust or Haskell.
|
||||
|
||||
> It's possible that it would be nice to present a note when printing these types
|
||||
> in a REPL or editor, e.g.
|
||||
>
|
||||
> ```
|
||||
> > \a, b -> a == b
|
||||
> <function> : a, a -> Bool
|
||||
>
|
||||
> Note: this `a` type variable will not accept types that contain functions
|
||||
> ```
|
||||
>
|
||||
> I can see a similar argument for noting information about whether a function
|
||||
> is tail-recursive. For example:
|
||||
>
|
||||
> ```
|
||||
> > fibonacci
|
||||
> <tail-recursive function> : Int -> Int
|
||||
> ```
|
||||
|
||||
## Unbound type variables
|
||||
|
||||
In Elm, every type variable is named. For example:
|
||||
@ -776,25 +732,54 @@ outside a record field. Optionality is a concept that exists only in record fiel
|
||||
and it's intended for the use case of config records like this. The ergonomics
|
||||
of destructuring mean this wouldn't be a good fit for data modeling.
|
||||
|
||||
## Function equality
|
||||
|
||||
In Elm, if you write `(\val -> val) == (\val -> val)`, you currently get a runtime exception
|
||||
which links to [the `==` docs](https://package.elm-lang.org/packages/elm/core/latest/Basics#==),
|
||||
which explain why this is the current behavior and what the better version will look like.
|
||||
|
||||
> OCaml also has the "runtime exception if you compare functions for structural equality"
|
||||
> behavior, but unlike Elm, in OCaml this appears to be the long-term design.
|
||||
|
||||
In Roc, function equality is a compile error, tracked explicitly in the type system.
|
||||
Here's the type of Roc's equality function:
|
||||
|
||||
```elm
|
||||
'val, 'val -> Bool
|
||||
```
|
||||
|
||||
Whenever a named type variable in Roc has a `'` at the beginning, that means
|
||||
it is a *functionless* type - a type which cannot involve functions.
|
||||
If there are any functions in that type, it's a type mismatch. This is true
|
||||
whether `val` itself is a function, or if it's a type that wraps a function,
|
||||
like `{ predicate: (Int -> Bool) }` or `List (Bool -> Bool)`.
|
||||
|
||||
So if you write `(\a -> a) == (\a -> a)` in Roc, you'll get a type mismatch.
|
||||
If you wrap both sides of that `==` in a record or list, you'll still get a
|
||||
type mismatch.
|
||||
|
||||
If a named type variable has a `'` anywhere in a given type, then it must have a `'`
|
||||
everywhere in that type. So it would be an error to have a type like `x, 'x -> Bool`
|
||||
because `x` has a `'` in one place but not everywhere.
|
||||
|
||||
## Standard Data Structures
|
||||
|
||||
Elm has `List`, `Array`, `Set`, and `Dict` in the standard library.
|
||||
|
||||
Roc has `List`, `Bytes`, `Set`, and `Map` in the standard library.
|
||||
Roc has `List`, `Set`, and `Map` in the standard library.
|
||||
|
||||
Here are the differences:
|
||||
|
||||
* `List` in Roc uses the term "list" the way Python does: to mean an unordered sequence of elements. Roc's `List` is more like Elm's `Array`; under the hood it is a RRB tree - specifically, [this one](https://docs.rs/im/14.0.0/im/vector/index.html). It still uses the `[` `]` syntax for values. Also there is no `::` operator because "cons" is not a notably efficient operation on a RRB tree like it is in a linked list; adding an element to either the beginning or end of a Roc `List` is an amortized constant time operation.
|
||||
* `Bytes` in Roc works like [`Bytes` in `elm/bytes`](https://package.elm-lang.org/packages/elm/bytes/latest/Bytes). It's in the standard library because Roc does not have a concept analogous to Elm's `Kernel`, so if `Bytes` weren't in the standard library, it could not exist as a separate package (and thus operating on raw byte streams would not be supported in Roc).
|
||||
* `Map` in Roc is like `Dict` in Elm, except it's backed by hashing rather than ordering. Like `List`, it uses [one of Bodil Stokke's `im-rs` persistent data structures](https://docs.rs/im/14.0.0/im/hashmap/index.html) under the hood. Roc also silently computes hash values for any value that can be used with `==`, so there is no `comparable` (or similar) constraint on `Map` keys in Roc.
|
||||
* `Set` in Roc is like `Set` in Elm: it's shorthand for a `Map` with keys but no value, and it has a slightly different API. Like with `Map`, there is no `comparable` (or similar) constraint on the values that can go in a Roc `Set`.
|
||||
* `List` in Roc uses the term "list" the way Python does: to mean an unordered sequence of elements. Roc's `List` is more like an array, in that all the elements are sequential in memory and can be accessed in constant time. It still uses the `[` `]` syntax for list literals. Also there is no `::` operator because "cons" is not an efficient operation on an array like it is in a linked list.
|
||||
* `Map` in Roc is like `Dict` in Elm, except it's backed by hashing rather than ordering. Roc silently computes hash values for any value that can be used with `==`, so instead of a `comparable` constraint on `Set` elements and `Map` keys, in Roc they instead have the *functionless* constraint indicated with a `'`. So to add to a `Set` you use `Set.add : Set 'elem, 'elem -> Set 'elem`, and putting a value into a Map is `Map.put : Map 'key val, 'key, val -> Map 'key val`.
|
||||
* `Set` in Roc is like `Set` in Elm: it's shorthand for a `Map` with keys but no value, and it has a slightly different API.
|
||||
|
||||
> The main reason it's called `Map` instead of `Dict` is that it's annoying to have a conversation about `Dict` out loud, let alone to teach it in a workshop, because you have to be so careful to enunciate. `Map` is one letter shorter, doesn't have this problem, is widely used, and never seems to be confused with the `map` function in practice (in e.g. JavaScript and Rust, both of which have both `Map` and `map`) even though it seems like it would in theory.
|
||||
|
||||
Roc also has a special literal syntax for maps and sets. Here's how to write a `Map` literal:
|
||||
Roc also has a literal syntax for maps and sets. Here's how to write a `Map` literal:
|
||||
|
||||
```elm
|
||||
{{ "Sam" => 1, "Ali" => 2, firstName => 3 }}
|
||||
{: "Sam" => 1, "Ali" => 2, firstName => 3 :}
|
||||
```
|
||||
|
||||
This expression has the type `Map Str Int`, and the `firstName` variable would
|
||||
@ -812,17 +797,18 @@ This works, but is not nearly as nice to read.
|
||||
|
||||
Additionally, map literals can compile direcly to efficient initialization code without needing to (hopefully be able to) optimize away the intermediate `List` involved in `fromList`.
|
||||
|
||||
`{{}}` is an empty `Map`.
|
||||
`{::}` is an empty `Map`.
|
||||
|
||||
You can write a `Set` literal like this:
|
||||
|
||||
```elm
|
||||
{[ "Sam", "Ali", firstName ]}
|
||||
[: "Sam", "Ali", firstName :]
|
||||
```
|
||||
|
||||
The `Set` literal syntax is partly for the initialization benefit, and also for symmetry
|
||||
with the `Map` literal syntax.
|
||||
|
||||
`{[]}` is an empty `Set`.
|
||||
`[::]` is an empty `Set`.
|
||||
|
||||
Roc does not have syntax for pattern matching on data structures - not even `[` `]` like Elm does.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user