Merge remote-tracking branch 'origin/main' into inspect-builtin

This commit is contained in:
Richard Feldman 2023-08-14 14:59:15 -04:00
commit 15a6bc34f4
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
183 changed files with 1793 additions and 1694 deletions

1
Cargo.lock generated
View file

@ -4099,6 +4099,7 @@ dependencies = [
"roc_debug_flags", "roc_debug_flags",
"roc_error_macros", "roc_error_macros",
"roc_module", "roc_module",
"roc_parse",
"roc_region", "roc_region",
"roc_serialize", "roc_serialize",
"static_assertions", "static_assertions",

84
FAQ.md
View file

@ -31,61 +31,6 @@ fantastical, and it has incredible potential for puns. Here are some different w
Fun fact: "roc" translates to 鹏 in Chinese, [which means](https://www.mdbg.net/chinese/dictionary?page=worddict&wdrst=0&wdqb=%E9%B9%8F) "a large fabulous bird." Fun fact: "roc" translates to 鹏 in Chinese, [which means](https://www.mdbg.net/chinese/dictionary?page=worddict&wdrst=0&wdqb=%E9%B9%8F) "a large fabulous bird."
## Why make a new editor instead of making an LSP plugin for VSCode, Vim or Emacs?
The Roc editor is one of the key areas where we want to innovate. Constraining ourselves to a plugin for existing editors would severely limit our possibilities for innovation.
A key part of our editor will be the use of plugins that are shipped with libraries. Think of a regex visualizer, parser debugger, or color picker. For library authors, it would be most convenient to write these plugins in Roc. Trying to dynamically load library plugins (written in Roc) in for example VSCode seems very difficult.
## Is there syntax highlighting for Vim/Emacs/VS Code or a LSP?
Not currently. Although they will presumably exist someday, while Roc is in the early days there's actually a conscious
effort to focus on the Roc Editor _instead of_ adding Roc support to other editors - specifically in order to give the Roc
Editor the best possible chance at kickstarting a virtuous cycle of plugin authorship.
This is an unusual approach, but there are more details in [this 2021 interview](https://youtu.be/ITrDd6-PbvY?t=212).
In the meantime, using CoffeeScript syntax highlighting for .roc files turns out to work surprisingly well!
## Why won't the editor be able to edit non-roc files like .md, .gitignore, .yml, ... ?
The downside of having the Roc editor support files other than .roc is that it seems extremely difficult to avoid scope creep if we allow it. For example, it starts with just editing json as plaintext but then it's annoying that there's no syntax highlighting, so maybe we add the capability to do syntax highlighting for json but of course then some people want it for toml, .md, etc, so we need to add a way to specify custom syntax highlighting rules for all of those.
Then of course people don't want to be copy/pasting syntax highlighting rules from online, so maybe someone develops a third party "plugin manager" for the editor to distribute these syntax highlighting definitions.
So maybe we add sharing syntax highlighting as a first-class thing, so people don't have to download a separate tool to use their editor normally but then some people who are using it for .json and .yaml start using it for .css too. Syntax highlighting is okay but it's annoying that they don't get error reporting when they mess up syntax or type an invalid selector or import and pretty soon there's demand for the Roc editor to do all the hardest parts of VS code.
We have to draw the line somewhere in there...but where to draw it?
It seems like drawing a bright line at .roc files is the most straightforward. It means the roc editor is the absolute best at editing .roc files and it isn't a weak editor for anything else because it doesn't try to be an editor for anything else and it means the scope is very clear.
## Why is there no way to specify "import everything this module exposes" in `imports`?
In [Elm](https://elm-lang.org), it's possible to import a module in a way that brings everything that module
exposes into scope. It can be convenient, but like all programming language features, it has downsides.
A minor reason Roc doesn't have this feature is that exposing everything can make it more difficult
outside the editor (e.g. on a website) to tell where something comes from, especially if multiple imports are
using this. ("I don't see `blah` defined in this module, so it must be coming from an import...but which of
these several import-exposing-everything modules could it be? I'll have to check all of them, or
download this code base and open it up in the editor so I can jump to definition!")
The main reason for this design, though, is compiler performance.
Currently, the name resolution step in compilation can be parallelized across modules, because it's possible to
tell if there's a naming error within a module using only the contents of that module. If "expose everything" is
allowed, then it's no longer clear whether anything is a naming error or not, until all the "expose everything"
modules have been processed, so we know exactly which names they expose. Because that feature doesn't exist in Roc,
all modules can do name resolution in parallel.
Of note, allowing this feature would only slow down modules that used it; modules that didn't use it would still be
parallelizable. However, when people find out ways to speed up their builds (in any language), advice starts to
circulate about how to unlock those speed boosts. If Roc had this feature, it's predictable that a commonly-accepted
piece of advice would eventually circulate: "don't use this feature because it slows down your builds."
If a feature exists in a language, but the common recommendation is never to use it, that's cause for reconsidering
whether the feature should be in the language at all. In the case of this feature, I think it's simpler if the
language doesn't have it; that way nobody has to learn (or spend time spreading the word) about the
performance-boosting advice not to use it.
## Why can't functions be compared for equality using the `==` operator? ## Why can't functions be compared for equality using the `==` operator?
Function equality has been proven to be undecidable in the general case because of the [halting problem](https://en.wikipedia.org/wiki/Halting_problem). Function equality has been proven to be undecidable in the general case because of the [halting problem](https://en.wikipedia.org/wiki/Halting_problem).
@ -126,6 +71,35 @@ The first of these problems could be addressed by having function equality alway
Each of these designs makes Roc a language that's some combination of more error-prone, more confusing, and more Each of these designs makes Roc a language that's some combination of more error-prone, more confusing, and more
brittle to change. Disallowing function equality at compile time eliminates all of these drawbacks. brittle to change. Disallowing function equality at compile time eliminates all of these drawbacks.
## Why is there no way to specify "import everything this module exposes" in `imports`?
In [Elm](https://elm-lang.org), it's possible to import a module in a way that brings everything that module
exposes into scope. It can be convenient, but like all programming language features, it has downsides.
A minor reason Roc doesn't have this feature is that exposing everything can make it more difficult
outside the editor (e.g. on a website) to tell where something comes from, especially if multiple imports are
using this. ("I don't see `blah` defined in this module, so it must be coming from an import...but which of
these several import-exposing-everything modules could it be? I'll have to check all of them, or
download this code base and open it up in the editor so I can jump to definition!")
The main reason for this design, though, is compiler performance.
Currently, the name resolution step in compilation can be parallelized across modules, because it's possible to
tell if there's a naming error within a module using only the contents of that module. If "expose everything" is
allowed, then it's no longer clear whether anything is a naming error or not, until all the "expose everything"
modules have been processed, so we know exactly which names they expose. Because that feature doesn't exist in Roc,
all modules can do name resolution in parallel.
Of note, allowing this feature would only slow down modules that used it; modules that didn't use it would still be
parallelizable. However, when people find out ways to speed up their builds (in any language), advice starts to
circulate about how to unlock those speed boosts. If Roc had this feature, it's predictable that a commonly-accepted
piece of advice would eventually circulate: "don't use this feature because it slows down your builds."
If a feature exists in a language, but the common recommendation is never to use it, that's cause for reconsidering
whether the feature should be in the language at all. In the case of this feature, I think it's simpler if the
language doesn't have it; that way nobody has to learn (or spend time spreading the word) about the
performance-boosting advice not to use it.
## Why doesn't Roc have a `Maybe` or `Option` or `Optional` type, or `null` or `nil` or `undefined`? ## Why doesn't Roc have a `Maybe` or `Option` or `Optional` type, or `null` or `nil` or `undefined`?
It's common for programming languages to have a [null reference](https://en.wikipedia.org/wiki/Null_pointer) It's common for programming languages to have a [null reference](https://en.wikipedia.org/wiki/Null_pointer)

View file

@ -10,9 +10,9 @@ Model position : {
openSet : Set position, openSet : Set position,
costs : Dict position F64, costs : Dict position F64,
cameFrom : Dict position position, cameFrom : Dict position position,
} | position has Hash & Eq } where position implements Hash & Eq
initialModel : position -> Model position | position has Hash & Eq initialModel : position -> Model position where position implements Hash & Eq
initialModel = \start -> { initialModel = \start -> {
evaluated: Set.empty {}, evaluated: Set.empty {},
openSet: Set.single start, openSet: Set.single start,
@ -20,7 +20,7 @@ initialModel = \start -> {
cameFrom: Dict.empty {}, cameFrom: Dict.empty {},
} }
cheapestOpen : (position -> F64), Model position -> Result position {} | position has Hash & Eq cheapestOpen : (position -> F64), Model position -> Result position {} where position implements Hash & Eq
cheapestOpen = \costFn, model -> cheapestOpen = \costFn, model ->
model.openSet model.openSet
|> Set.toList |> Set.toList
@ -35,13 +35,13 @@ cheapestOpen = \costFn, model ->
|> Result.map .position |> Result.map .position
|> Result.mapErr (\_ -> {}) |> Result.mapErr (\_ -> {})
reconstructPath : Dict position position, position -> List position | position has Hash & Eq reconstructPath : Dict position position, position -> List position where position implements Hash & Eq
reconstructPath = \cameFrom, goal -> reconstructPath = \cameFrom, goal ->
when Dict.get cameFrom goal is when Dict.get cameFrom goal is
Err _ -> [] Err _ -> []
Ok next -> List.append (reconstructPath cameFrom next) goal Ok next -> List.append (reconstructPath cameFrom next) goal
updateCost : position, position, Model position -> Model position | position has Hash & Eq updateCost : position, position, Model position -> Model position where position implements Hash & Eq
updateCost = \current, neighbor, model -> updateCost = \current, neighbor, model ->
newCameFrom = newCameFrom =
Dict.insert model.cameFrom neighbor current Dict.insert model.cameFrom neighbor current
@ -70,7 +70,7 @@ updateCost = \current, neighbor, model ->
else else
model model
astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {} | position has Hash & Eq astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {} where position implements Hash & Eq
astar = \costFn, moveFn, goal, model -> astar = \costFn, moveFn, goal, model ->
when cheapestOpen (\source -> costFn source goal) model is when cheapestOpen (\source -> costFn source goal) model is
Err {} -> Err {} Err {} -> Err {}

View file

@ -4,6 +4,7 @@ use roc_command_utils::{cargo, clang, rustup, zig};
use roc_error_macros::internal_error; use roc_error_macros::internal_error;
use roc_mono::ir::OptLevel; use roc_mono::ir::OptLevel;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsString;
use std::fs::DirEntry; use std::fs::DirEntry;
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -785,11 +786,25 @@ fn get_target_str(target: &Triple) -> &str {
} }
} }
fn nix_path_opt() -> Option<String> { fn nix_paths() -> Vec<String> {
env::var_os("NIX_GLIBC_PATH").map(|path| path.into_string().unwrap()) let mut paths = vec![];
if let Some(nix_libgcc_s_path) = env::var_os("NIX_LIBGCC_S_PATH") {
paths.push(nix_libgcc_s_path.into_string().unwrap())
} }
fn library_path<const N: usize>(segments: [&str; N]) -> Option<PathBuf> { if let Some(nix_glibc_path) = nix_glibc_path_opt() {
paths.push(nix_glibc_path.into_string().unwrap())
}
paths
}
fn nix_glibc_path_opt() -> Option<OsString> {
env::var_os("NIX_GLIBC_PATH")
}
fn build_path<const N: usize>(segments: [&str; N]) -> Option<PathBuf> {
let mut guess_path = PathBuf::new(); let mut guess_path = PathBuf::new();
for s in segments { for s in segments {
guess_path.push(s); guess_path.push(s);
@ -812,22 +827,21 @@ fn library_path<const N: usize>(segments: [&str; N]) -> Option<PathBuf> {
/// match will be returned. /// match will be returned.
/// ///
/// If there are no matches, [`None`] will be returned. /// If there are no matches, [`None`] will be returned.
fn look_for_library(lib_dirs: &[&[&str]], lib_filename: &str) -> Option<PathBuf> { fn look_for_library(lib_dirs: &[PathBuf], lib_filename: &str) -> Option<PathBuf> {
lib_dirs lib_dirs
.iter() .iter()
.map(|lib_dir| { .map(|path| {
lib_dir.iter().fold(PathBuf::new(), |mut path, segment| { let mut path_cl = path.clone();
path.push(segment); path_cl.push(lib_filename);
path path_cl
})
})
.map(|mut path| {
path.push(lib_filename);
path
}) })
.find(|path| path.exists()) .find(|path| path.exists())
} }
fn strs_to_path(strs: &[&str]) -> PathBuf {
strs.iter().collect()
}
fn link_linux( fn link_linux(
target: &Triple, target: &Triple,
output_path: PathBuf, output_path: PathBuf,
@ -862,50 +876,37 @@ fn link_linux(
)); ));
} }
// Some things we'll need to build a list of dirs to check for libraries let nix_paths_vec_string = nix_paths();
let maybe_nix_path = nix_path_opt(); let nix_paths_vec: Vec<PathBuf> = nix_paths_vec_string.iter().map(PathBuf::from).collect();
let usr_lib_arch = ["/usr", "lib", &architecture]; let usr_lib_arch_path = strs_to_path(&["/usr", "lib", &architecture]);
let lib_arch = ["/lib", &architecture]; let lib_arch_path = strs_to_path(&["/lib", &architecture]);
let nix_path_segments;
let lib_dirs_if_nix: [&[&str]; 5];
let lib_dirs_if_nonix: [&[&str]; 4];
// Build the aformentioned list let mut lib_dirs: Vec<PathBuf> = vec![];
let lib_dirs: &[&[&str]] =
// give preference to nix_path if it's defined, this prevents bugs // start with nix paths, this prevents version incompatibility
if let Some(nix_path) = &maybe_nix_path { if !nix_paths_vec.is_empty() {
nix_path_segments = [nix_path.as_str()]; lib_dirs.extend(nix_paths_vec)
lib_dirs_if_nix = [ }
&nix_path_segments,
&usr_lib_arch, lib_dirs.extend([
&lib_arch, usr_lib_arch_path,
&["/usr", "lib"], lib_arch_path,
&["/usr", "lib64"], strs_to_path(&["/usr", "lib"]),
]; strs_to_path(&["/usr", "lib64"]),
&lib_dirs_if_nix ]);
} else {
lib_dirs_if_nonix = [
&usr_lib_arch,
&lib_arch,
&["/usr", "lib"],
&["/usr", "lib64"],
];
&lib_dirs_if_nonix
};
// Look for the libraries we'll need // Look for the libraries we'll need
let libgcc_name = "libgcc_s.so.1"; let libgcc_name = "libgcc_s.so.1";
let libgcc_path = look_for_library(lib_dirs, libgcc_name); let libgcc_path = look_for_library(&lib_dirs, libgcc_name);
let crti_name = "crti.o"; let crti_name = "crti.o";
let crti_path = look_for_library(lib_dirs, crti_name); let crti_path = look_for_library(&lib_dirs, crti_name);
let crtn_name = "crtn.o"; let crtn_name = "crtn.o";
let crtn_path = look_for_library(lib_dirs, crtn_name); let crtn_path = look_for_library(&lib_dirs, crtn_name);
let scrt1_name = "Scrt1.o"; let scrt1_name = "Scrt1.o";
let scrt1_path = look_for_library(lib_dirs, scrt1_name); let scrt1_path = look_for_library(&lib_dirs, scrt1_name);
// Unwrap all the paths at once so we can inform the user of all missing libs at once // Unwrap all the paths at once so we can inform the user of all missing libs at once
let (libgcc_path, crti_path, crtn_path, scrt1_path) = let (libgcc_path, crti_path, crtn_path, scrt1_path) =
@ -925,7 +926,13 @@ fn link_linux(
let dirs = lib_dirs let dirs = lib_dirs
.iter() .iter()
.map(|segments| segments.join("/")) .map(|path_buf| {
path_buf
.as_path()
.to_str()
.unwrap_or("FAILED TO CONVERT PATH TO STR")
.to_string()
})
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n"); .join("\n");
eprintln!("We looked in the following directories:\n{dirs}"); eprintln!("We looked in the following directories:\n{dirs}");
@ -936,13 +943,16 @@ fn link_linux(
let ld_linux = match target.architecture { let ld_linux = match target.architecture {
Architecture::X86_64 => { Architecture::X86_64 => {
// give preference to nix_path if it's defined, this prevents bugs // give preference to nix_path if it's defined, this prevents bugs
if let Some(nix_path) = nix_path_opt() { if let Some(nix_glibc_path) = nix_glibc_path_opt() {
library_path([&nix_path, "ld-linux-x86-64.so.2"]) build_path([
&nix_glibc_path.into_string().unwrap(),
"ld-linux-x86-64.so.2",
])
} else { } else {
library_path(["/lib64", "ld-linux-x86-64.so.2"]) build_path(["/lib64", "ld-linux-x86-64.so.2"])
} }
} }
Architecture::Aarch64(_) => library_path(["/lib", "ld-linux-aarch64.so.1"]), Architecture::Aarch64(_) => build_path(["/lib", "ld-linux-aarch64.so.1"]),
_ => internal_error!( _ => internal_error!(
"TODO gracefully handle unsupported linux architecture: {:?}", "TODO gracefully handle unsupported linux architecture: {:?}",
target.architecture target.architecture

View file

@ -12,7 +12,7 @@ interface Bool
## be a `NaN` ([Not a Number](https://en.wikipedia.org/wiki/NaN)), and the ## be a `NaN` ([Not a Number](https://en.wikipedia.org/wiki/NaN)), and the
## [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating point standard ## [IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) floating point standard
## specifies that two `NaN`s are not equal. ## specifies that two `NaN`s are not equal.
Eq has Eq implements
## Returns `Bool.true` if the input values are equal. This is ## Returns `Bool.true` if the input values are equal. This is
## equivalent to the logic ## equivalent to the logic
## [XNOR](https://en.wikipedia.org/wiki/Logical_equality) gate. The infix ## [XNOR](https://en.wikipedia.org/wiki/Logical_equality) gate. The infix
@ -30,11 +30,11 @@ Eq has
## for more detail. ## for more detail.
## 5. Functions cannot be compared for structural equality, therefore Roc ## 5. Functions cannot be compared for structural equality, therefore Roc
## cannot derive `isEq` for types that contain functions. ## cannot derive `isEq` for types that contain functions.
isEq : a, a -> Bool | a has Eq isEq : a, a -> Bool where a implements Eq
## Represents the boolean true and false using an opaque type. ## Represents the boolean true and false using an opaque type.
## `Bool` implements the `Eq` ability. ## `Bool` implements the `Eq` ability.
Bool := [True, False] has [Eq { isEq: boolIsEq }] Bool := [True, False] implements [Eq { isEq: boolIsEq }]
boolIsEq = \@Bool b1, @Bool b2 -> structuralEq b1 b2 boolIsEq = \@Bool b1, @Bool b2 -> structuralEq b1 b2
@ -116,7 +116,7 @@ not : Bool -> Bool
## expect (Bool.false != Bool.false) == Bool.false ## expect (Bool.false != Bool.false) == Bool.false
## expect "Apples" != "Oranges" ## expect "Apples" != "Oranges"
## ``` ## ```
isNotEq : a, a -> Bool | a has Eq isNotEq : a, a -> Bool where a implements Eq
isNotEq = \a, b -> structuralNotEq a b isNotEq = \a, b -> structuralNotEq a b
# INTERNAL COMPILER USE ONLY: used to lower calls to `isEq` to structural # INTERNAL COMPILER USE ONLY: used to lower calls to `isEq` to structural

View file

@ -73,30 +73,30 @@ DecodeResult val : { result : Result val DecodeError, rest : List U8 }
## Decodes a `List U8` of utf-8 bytes where `val` is the type of the decoded ## Decodes a `List U8` of utf-8 bytes where `val` is the type of the decoded
## value, and `fmt` is a [Decoder] which implements the [DecoderFormatting] ## value, and `fmt` is a [Decoder] which implements the [DecoderFormatting]
## ability ## ability
Decoder val fmt := List U8, fmt -> DecodeResult val | fmt has DecoderFormatting Decoder val fmt := List U8, fmt -> DecodeResult val where fmt implements DecoderFormatting
## Definition of the [Decoding] ability ## Definition of the [Decoding] ability
Decoding has Decoding implements
decoder : Decoder val fmt | val has Decoding, fmt has DecoderFormatting decoder : Decoder val fmt where val implements Decoding, fmt implements DecoderFormatting
## Definition of the [DecoderFormatting] ability ## Definition of the [DecoderFormatting] ability
DecoderFormatting has DecoderFormatting implements
u8 : Decoder U8 fmt | fmt has DecoderFormatting u8 : Decoder U8 fmt where fmt implements DecoderFormatting
u16 : Decoder U16 fmt | fmt has DecoderFormatting u16 : Decoder U16 fmt where fmt implements DecoderFormatting
u32 : Decoder U32 fmt | fmt has DecoderFormatting u32 : Decoder U32 fmt where fmt implements DecoderFormatting
u64 : Decoder U64 fmt | fmt has DecoderFormatting u64 : Decoder U64 fmt where fmt implements DecoderFormatting
u128 : Decoder U128 fmt | fmt has DecoderFormatting u128 : Decoder U128 fmt where fmt implements DecoderFormatting
i8 : Decoder I8 fmt | fmt has DecoderFormatting i8 : Decoder I8 fmt where fmt implements DecoderFormatting
i16 : Decoder I16 fmt | fmt has DecoderFormatting i16 : Decoder I16 fmt where fmt implements DecoderFormatting
i32 : Decoder I32 fmt | fmt has DecoderFormatting i32 : Decoder I32 fmt where fmt implements DecoderFormatting
i64 : Decoder I64 fmt | fmt has DecoderFormatting i64 : Decoder I64 fmt where fmt implements DecoderFormatting
i128 : Decoder I128 fmt | fmt has DecoderFormatting i128 : Decoder I128 fmt where fmt implements DecoderFormatting
f32 : Decoder F32 fmt | fmt has DecoderFormatting f32 : Decoder F32 fmt where fmt implements DecoderFormatting
f64 : Decoder F64 fmt | fmt has DecoderFormatting f64 : Decoder F64 fmt where fmt implements DecoderFormatting
dec : Decoder Dec fmt | fmt has DecoderFormatting dec : Decoder Dec fmt where fmt implements DecoderFormatting
bool : Decoder Bool fmt | fmt has DecoderFormatting bool : Decoder Bool fmt where fmt implements DecoderFormatting
string : Decoder Str fmt | fmt has DecoderFormatting string : Decoder Str fmt where fmt implements DecoderFormatting
list : Decoder elem fmt -> Decoder (List elem) fmt | fmt has DecoderFormatting list : Decoder elem fmt -> Decoder (List elem) fmt where fmt implements DecoderFormatting
## `record state stepField finalizer` decodes a record field-by-field. ## `record state stepField finalizer` decodes a record field-by-field.
## ##
@ -104,7 +104,7 @@ DecoderFormatting has
## `Skip` if the field is not a part of the decoded record. ## `Skip` if the field is not a part of the decoded record.
## ##
## `finalizer` should produce the record value from the decoded `state`. ## `finalizer` should produce the record value from the decoded `state`.
record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting record : state, (state, Str -> [Keep (Decoder state fmt), Skip]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
## `tuple state stepElem finalizer` decodes a tuple element-by-element. ## `tuple state stepElem finalizer` decodes a tuple element-by-element.
## ##
@ -113,7 +113,7 @@ DecoderFormatting has
## index passed to `stepElem` is 0-indexed. ## index passed to `stepElem` is 0-indexed.
## ##
## `finalizer` should produce the tuple value from the decoded `state`. ## `finalizer` should produce the tuple value from the decoded `state`.
tuple : state, (state, Nat -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt | fmt has DecoderFormatting tuple : state, (state, Nat -> [Next (Decoder state fmt), TooLong]), (state -> Result val DecodeError) -> Decoder val fmt where fmt implements DecoderFormatting
## Build a custom [Decoder] function. For example the implementation of ## Build a custom [Decoder] function. For example the implementation of
## `decodeBool` could be defined as follows; ## `decodeBool` could be defined as follows;
@ -125,11 +125,11 @@ DecoderFormatting has
## ['t', 'r', 'u', 'e', ..] -> { result: Ok Bool.true, rest: List.drop bytes 4 } ## ['t', 'r', 'u', 'e', ..] -> { result: Ok Bool.true, rest: List.drop bytes 4 }
## _ -> { result: Err TooShort, rest: bytes } ## _ -> { result: Err TooShort, rest: bytes }
## ``` ## ```
custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting custom : (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
custom = \decode -> @Decoder decode custom = \decode -> @Decoder decode
## Decode a `List U8` utf-8 bytes using a specific [Decoder] function ## Decode a `List U8` utf-8 bytes using a specific [Decoder] function
decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt
## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult) ## Decode a `List U8` utf-8 bytes and return a [DecodeResult](#DecodeResult)
@ -141,7 +141,7 @@ decodeWith = \bytes, @Decoder decode, fmt -> decode bytes fmt
## ##
## actual.result == expected ## actual.result == expected
## ``` ## ```
fromBytesPartial : List U8, fmt -> DecodeResult val | val has Decoding, fmt has DecoderFormatting fromBytesPartial : List U8, fmt -> DecodeResult val where val implements Decoding, fmt implements DecoderFormatting
fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt
## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes ## Decode a `List U8` utf-8 bytes and return a [Result] with no leftover bytes
@ -155,7 +155,7 @@ fromBytesPartial = \bytes, fmt -> decodeWith bytes decoder fmt
## ##
## actual == expected ## actual == expected
## ``` ## ```
fromBytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError | val has Decoding, fmt has DecoderFormatting fromBytes : List U8, fmt -> Result val [Leftover (List U8)]DecodeError where val implements Decoding, fmt implements DecoderFormatting
fromBytes = \bytes, fmt -> fromBytes = \bytes, fmt ->
when fromBytesPartial bytes fmt is when fromBytesPartial bytes fmt is
{ result, rest } -> { result, rest } ->

View file

@ -95,13 +95,13 @@ Dict k v := {
# TODO: As an optimization, we can make all of these lists in one allocation # TODO: As an optimization, we can make all of these lists in one allocation
# TODO: Grow data with the rest of the hashmap. This will require creating a list of garbage data. # TODO: Grow data with the rest of the hashmap. This will require creating a list of garbage data.
# TODO: Change remove to use tombstones. Store the tombstones in a bitmap. # TODO: Change remove to use tombstones. Store the tombstones in a bitmap.
# TODO: define Eq and Hash that are unordered. Only if value has hash/eq? # TODO: define Eq and Hash that are unordered. Only if value implements hash/eq?
metadata : List I8, metadata : List I8,
dataIndices : List Nat, dataIndices : List Nat,
data : List (k, v), data : List (k, v),
size : Nat, size : Nat,
} | k has Hash & Eq } where k implements Hash & Eq
has [ implements [
Eq { Eq {
isEq, isEq,
}, },
@ -110,7 +110,7 @@ Dict k v := {
}, },
] ]
isEq : Dict k v, Dict k v -> Bool | k has Hash & Eq, v has Eq isEq : Dict k v, Dict k v -> Bool where k implements Hash & Eq, v implements Eq
isEq = \xs, ys -> isEq = \xs, ys ->
if len xs != len ys then if len xs != len ys then
Bool.false Bool.false
@ -123,14 +123,14 @@ isEq = \xs, ys ->
_ -> _ ->
Break Bool.false Break Bool.false
hashDict : hasher, Dict k v -> hasher | k has Hash & Eq, v has Hash, hasher has Hasher hashDict : hasher, Dict k v -> hasher where k implements Hash & Eq, v implements Hash, hasher implements Hasher
hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk hashDict = \hasher, dict -> Hash.hashUnordered hasher (toList dict) List.walk
## Return an empty dictionary. ## Return an empty dictionary.
## ``` ## ```
## emptyDict = Dict.empty {} ## emptyDict = Dict.empty {}
## ``` ## ```
empty : {} -> Dict k v | k has Hash & Eq empty : {} -> Dict k v where k implements Hash & Eq
empty = \{} -> empty = \{} ->
@Dict { @Dict {
metadata: List.repeat emptySlot 8, metadata: List.repeat emptySlot 8,
@ -156,7 +156,7 @@ capacity = \@Dict { dataIndices } ->
## Return a dictionary with space allocated for a number of entries. This ## Return a dictionary with space allocated for a number of entries. This
## may provide a performance optimization if you know how many entries will be ## may provide a performance optimization if you know how many entries will be
## inserted. ## inserted.
withCapacity : Nat -> Dict k v | k has Hash & Eq withCapacity : Nat -> Dict k v where k implements Hash & Eq
withCapacity = \_ -> withCapacity = \_ ->
# TODO: power of 2 * 8 and actual implementation # TODO: power of 2 * 8 and actual implementation
empty {} empty {}
@ -167,7 +167,7 @@ withCapacity = \_ ->
## Dict.single "A" "B" ## Dict.single "A" "B"
## |> Bool.isEq (Dict.insert (Dict.empty {}) "A" "B") ## |> Bool.isEq (Dict.insert (Dict.empty {}) "A" "B")
## ``` ## ```
single : k, v -> Dict k v | k has Hash & Eq single : k, v -> Dict k v where k implements Hash & Eq
single = \k, v -> single = \k, v ->
insert (empty {}) k v insert (empty {}) k v
@ -180,7 +180,7 @@ single = \k, v ->
## |> Dict.insert 4 "Four" ## |> Dict.insert 4 "Four"
## |> Bool.isEq (Dict.fromList [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")]) ## |> Bool.isEq (Dict.fromList [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")])
## ``` ## ```
fromList : List (k, v) -> Dict k v | k has Hash & Eq fromList : List (k, v) -> Dict k v where k implements Hash & Eq
fromList = \data -> fromList = \data ->
# TODO: make this efficient. Should just set data and then set all indicies in the hashmap. # TODO: make this efficient. Should just set data and then set all indicies in the hashmap.
List.walk data (empty {}) (\dict, (k, v) -> insert dict k v) List.walk data (empty {}) (\dict, (k, v) -> insert dict k v)
@ -221,7 +221,7 @@ isEmpty = \@Dict { size } ->
## ##
## expect Dict.len clearSongs == 0 ## expect Dict.len clearSongs == 0
## ``` ## ```
clear : Dict k v -> Dict k v | k has Hash & Eq clear : Dict k v -> Dict k v where k implements Hash & Eq
clear = \@Dict { metadata, dataIndices, data } -> clear = \@Dict { metadata, dataIndices, data } ->
cap = List.len dataIndices cap = List.len dataIndices
@ -241,7 +241,7 @@ clear = \@Dict { metadata, dataIndices, data } ->
## Convert each value in the dictionary to something new, by calling a conversion ## Convert each value in the dictionary to something new, by calling a conversion
## function on each of them which receives both the key and the old value. Then return a ## function on each of them which receives both the key and the old value. Then return a
## new dictionary containing the same keys and the converted values. ## new dictionary containing the same keys and the converted values.
map : Dict k a, (k, a -> b) -> Dict k b | k has Hash & Eq, b has Hash & Eq map : Dict k a, (k, a -> b) -> Dict k b where k implements Hash & Eq, b implements Hash & Eq
map = \dict, transform -> map = \dict, transform ->
init = withCapacity (capacity dict) init = withCapacity (capacity dict)
@ -253,7 +253,7 @@ map = \dict, transform ->
## (using [Dict.insertAll]) into one dictionary. ## (using [Dict.insertAll]) into one dictionary.
## ##
## You may know a similar function named `concatMap` in other languages. ## You may know a similar function named `concatMap` in other languages.
joinMap : Dict a b, (a, b -> Dict x y) -> Dict x y | a has Hash & Eq, x has Hash & Eq joinMap : Dict a b, (a, b -> Dict x y) -> Dict x y where a implements Hash & Eq, x implements Hash & Eq
joinMap = \dict, transform -> joinMap = \dict, transform ->
init = withCapacity (capacity dict) # Might be a pessimization init = withCapacity (capacity dict) # Might be a pessimization
@ -271,7 +271,7 @@ joinMap = \dict, transform ->
## |> Dict.walk 0 (\count, _, qty -> count + qty) ## |> Dict.walk 0 (\count, _, qty -> count + qty)
## |> Bool.isEq 36 ## |> Bool.isEq 36
## ``` ## ```
walk : Dict k v, state, (state, k, v -> state) -> state | k has Hash & Eq walk : Dict k v, state, (state, k, v -> state) -> state where k implements Hash & Eq
walk = \@Dict { data }, initialState, transform -> walk = \@Dict { data }, initialState, transform ->
List.walk data initialState (\state, (k, v) -> transform state k v) List.walk data initialState (\state, (k, v) -> transform state k v)
@ -303,7 +303,7 @@ walk = \@Dict { data }, initialState, transform ->
## ##
## expect someoneIsAnAdult == Bool.true ## expect someoneIsAnAdult == Bool.true
## ``` ## ```
walkUntil : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state | k has Hash & Eq walkUntil : Dict k v, state, (state, k, v -> [Continue state, Break state]) -> state where k implements Hash & Eq
walkUntil = \@Dict { data }, initialState, transform -> walkUntil = \@Dict { data }, initialState, transform ->
List.walkUntil data initialState (\state, (k, v) -> transform state k v) List.walkUntil data initialState (\state, (k, v) -> transform state k v)
@ -318,7 +318,7 @@ walkUntil = \@Dict { data }, initialState, transform ->
## expect Dict.get dictionary 1 == Ok "Apple" ## expect Dict.get dictionary 1 == Ok "Apple"
## expect Dict.get dictionary 2000 == Err KeyNotFound ## expect Dict.get dictionary 2000 == Err KeyNotFound
## ``` ## ```
get : Dict k v, k -> Result v [KeyNotFound] | k has Hash & Eq get : Dict k v, k -> Result v [KeyNotFound] where k implements Hash & Eq
get = \@Dict { metadata, dataIndices, data }, key -> get = \@Dict { metadata, dataIndices, data }, key ->
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
@ -346,7 +346,7 @@ get = \@Dict { metadata, dataIndices, data }, key ->
## |> Dict.contains 1234 ## |> Dict.contains 1234
## |> Bool.isEq Bool.true ## |> Bool.isEq Bool.true
## ``` ## ```
contains : Dict k v, k -> Bool | k has Hash & Eq contains : Dict k v, k -> Bool where k implements Hash & Eq
contains = \@Dict { metadata, dataIndices, data }, key -> contains = \@Dict { metadata, dataIndices, data }, key ->
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
@ -371,7 +371,7 @@ contains = \@Dict { metadata, dataIndices, data }, key ->
## |> Dict.get "Apples" ## |> Dict.get "Apples"
## |> Bool.isEq (Ok 12) ## |> Bool.isEq (Ok 12)
## ``` ## ```
insert : Dict k v, k, v -> Dict k v | k has Hash & Eq insert : Dict k v, k, v -> Dict k v where k implements Hash & Eq
insert = \@Dict { metadata, dataIndices, data, size }, key, value -> insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
@ -417,7 +417,7 @@ insert = \@Dict { metadata, dataIndices, data, size }, key, value ->
## |> Dict.len ## |> Dict.len
## |> Bool.isEq 0 ## |> Bool.isEq 0
## ``` ## ```
remove : Dict k v, k -> Dict k v | k has Hash & Eq remove : Dict k v, k -> Dict k v where k implements Hash & Eq
remove = \@Dict { metadata, dataIndices, data, size }, key -> remove = \@Dict { metadata, dataIndices, data, size }, key ->
# TODO: change this from swap remove to tombstone and test is performance is still good. # TODO: change this from swap remove to tombstone and test is performance is still good.
hashKey = hashKey =
@ -461,7 +461,7 @@ remove = \@Dict { metadata, dataIndices, data, size }, key ->
## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true ## expect Dict.update (Dict.single "a" Bool.false) "a" alterValue == Dict.single "a" Bool.true
## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty {} ## expect Dict.update (Dict.single "a" Bool.true) "a" alterValue == Dict.empty {}
## ``` ## ```
update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v | k has Hash & Eq update : Dict k v, k, ([Present v, Missing] -> [Present v, Missing]) -> Dict k v where k implements Hash & Eq
update = \dict, key, alter -> update = \dict, key, alter ->
# TODO: look into optimizing by merging substeps and reducing lookups. # TODO: look into optimizing by merging substeps and reducing lookups.
possibleValue = possibleValue =
@ -484,7 +484,7 @@ update = \dict, key, alter ->
## |> Dict.toList ## |> Dict.toList
## |> Bool.isEq [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")] ## |> Bool.isEq [(1, "One"), (2, "Two"), (3, "Three"), (4, "Four")]
## ``` ## ```
toList : Dict k v -> List (k, v) | k has Hash & Eq toList : Dict k v -> List (k, v) where k implements Hash & Eq
toList = \@Dict { data } -> toList = \@Dict { data } ->
data data
@ -499,7 +499,7 @@ toList = \@Dict { data } ->
## |> Dict.keys ## |> Dict.keys
## |> Bool.isEq [1,2,3,4] ## |> Bool.isEq [1,2,3,4]
## ``` ## ```
keys : Dict k v -> List k | k has Hash & Eq keys : Dict k v -> List k where k implements Hash & Eq
keys = \@Dict { data } -> keys = \@Dict { data } ->
List.map data (\(k, _) -> k) List.map data (\(k, _) -> k)
@ -514,7 +514,7 @@ keys = \@Dict { data } ->
## |> Dict.values ## |> Dict.values
## |> Bool.isEq ["One","Two","Three","Four"] ## |> Bool.isEq ["One","Two","Three","Four"]
## ``` ## ```
values : Dict k v -> List v | k has Hash & Eq values : Dict k v -> List v where k implements Hash & Eq
values = \@Dict { data } -> values = \@Dict { data } ->
List.map data (\(_, v) -> v) List.map data (\(_, v) -> v)
@ -542,7 +542,7 @@ values = \@Dict { data } ->
## expect ## expect
## Dict.insertAll first second == expected ## Dict.insertAll first second == expected
## ``` ## ```
insertAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq insertAll : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
insertAll = \xs, ys -> insertAll = \xs, ys ->
walk ys xs insert walk ys xs insert
@ -564,7 +564,7 @@ insertAll = \xs, ys ->
## ##
## expect Dict.keepShared first second == first ## expect Dict.keepShared first second == first
## ``` ## ```
keepShared : Dict k v, Dict k v -> Dict k v | k has Hash & Eq keepShared : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
keepShared = \xs, ys -> keepShared = \xs, ys ->
walk walk
xs xs
@ -596,11 +596,11 @@ keepShared = \xs, ys ->
## ##
## expect Dict.removeAll first second == expected ## expect Dict.removeAll first second == expected
## ``` ## ```
removeAll : Dict k v, Dict k v -> Dict k v | k has Hash & Eq removeAll : Dict k v, Dict k v -> Dict k v where k implements Hash & Eq
removeAll = \xs, ys -> removeAll = \xs, ys ->
walk ys xs (\state, k, _ -> remove state k) walk ys xs (\state, k, _ -> remove state k)
swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v | k has Hash & Eq swapAndUpdateDataIndex : Dict k v, Nat, Nat -> Dict k v where k implements Hash & Eq
swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex -> swapAndUpdateDataIndex = \@Dict { metadata, dataIndices, data, size }, removedIndex, lastIndex ->
(key, _) = listGetUnsafe data lastIndex (key, _) = listGetUnsafe data lastIndex
hashKey = hashKey =
@ -664,7 +664,7 @@ nextEmptyOrDeletedHelper = \metadata, probe, offset ->
# TODO: investigate if this needs to be split into more specific helper functions. # TODO: investigate if this needs to be split into more specific helper functions.
# There is a chance that returning specific sub-info like the value would be faster. # There is a chance that returning specific sub-info like the value would be faster.
findIndexHelper : List I8, List Nat, List (k, v), I8, k, Probe, Nat -> Result Nat [NotFound] | k has Hash & Eq findIndexHelper : List I8, List Nat, List (k, v), I8, k, Probe, Nat -> Result Nat [NotFound] where k implements Hash & Eq
findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset -> findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
# For finding a value, we must search past all deleted element tombstones. # For finding a value, we must search past all deleted element tombstones.
index = Num.addWrap (mul8 probe.slotIndex) offset index = Num.addWrap (mul8 probe.slotIndex) offset
@ -696,7 +696,7 @@ findIndexHelper = \metadata, dataIndices, data, h2Key, key, probe, offset ->
# This is how we grow the container. # This is how we grow the container.
# If we aren't to the load factor yet, just ignore this. # If we aren't to the load factor yet, just ignore this.
# The container must have an updated size including any elements about to be inserted. # The container must have an updated size including any elements about to be inserted.
maybeRehash : Dict k v -> Dict k v | k has Hash & Eq maybeRehash : Dict k v -> Dict k v where k implements Hash & Eq
maybeRehash = \@Dict { metadata, dataIndices, data, size } -> maybeRehash = \@Dict { metadata, dataIndices, data, size } ->
cap = List.len dataIndices cap = List.len dataIndices
maxLoadCap = maxLoadCap =
@ -709,7 +709,7 @@ maybeRehash = \@Dict { metadata, dataIndices, data, size } ->
@Dict { metadata, dataIndices, data, size } @Dict { metadata, dataIndices, data, size }
# TODO: switch rehash to iterate data and eventually clear out tombstones as well. # TODO: switch rehash to iterate data and eventually clear out tombstones as well.
rehash : Dict k v -> Dict k v | k has Hash & Eq rehash : Dict k v -> Dict k v where k implements Hash & Eq
rehash = \@Dict { metadata, dataIndices, data, size } -> rehash = \@Dict { metadata, dataIndices, data, size } ->
newLen = 2 * List.len dataIndices newLen = 2 * List.len dataIndices
newDict = newDict =
@ -722,7 +722,7 @@ rehash = \@Dict { metadata, dataIndices, data, size } ->
rehashHelper newDict metadata dataIndices data 0 rehashHelper newDict metadata dataIndices data 0
rehashHelper : Dict k v, List I8, List Nat, List (k, v), Nat -> Dict k v | k has Hash & Eq rehashHelper : Dict k v, List I8, List Nat, List (k, v), Nat -> Dict k v where k implements Hash & Eq
rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index -> rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
when List.get oldMetadata index is when List.get oldMetadata index is
Ok md -> Ok md ->
@ -743,7 +743,7 @@ rehashHelper = \dict, oldMetadata, oldDataIndices, oldData, index ->
# Walked entire list, complete now. # Walked entire list, complete now.
dict dict
insertForRehash : Dict k v, k, Nat -> Dict k v | k has Hash & Eq insertForRehash : Dict k v, k, Nat -> Dict k v where k implements Hash & Eq
insertForRehash = \@Dict { metadata, dataIndices, data, size }, key, dataIndex -> insertForRehash = \@Dict { metadata, dataIndices, data, size }, key, dataIndex ->
hashKey = hashKey =
createLowLevelHasher PseudoRandSeed createLowLevelHasher PseudoRandSeed
@ -1010,7 +1010,7 @@ expect
# TODO: wyhash is slow for large keys, use something like cityhash if the keys are too long. # TODO: wyhash is slow for large keys, use something like cityhash if the keys are too long.
# TODO: Add a builtin to distinguish big endian systems and change loading orders. # TODO: Add a builtin to distinguish big endian systems and change loading orders.
# TODO: Switch out Wymum on systems with slow 128bit multiplication. # TODO: Switch out Wymum on systems with slow 128bit multiplication.
LowLevelHasher := { originalSeed : U64, state : U64 } has [ LowLevelHasher := { originalSeed : U64, state : U64 } implements [
Hasher { Hasher {
addBytes, addBytes,
addU8, addU8,

View file

@ -47,40 +47,40 @@ interface Encode
Bool.{ Bool }, Bool.{ Bool },
] ]
Encoder fmt := List U8, fmt -> List U8 | fmt has EncoderFormatting Encoder fmt := List U8, fmt -> List U8 where fmt implements EncoderFormatting
Encoding has Encoding implements
toEncoder : val -> Encoder fmt | val has Encoding, fmt has EncoderFormatting toEncoder : val -> Encoder fmt where val implements Encoding, fmt implements EncoderFormatting
EncoderFormatting has EncoderFormatting implements
u8 : U8 -> Encoder fmt | fmt has EncoderFormatting u8 : U8 -> Encoder fmt where fmt implements EncoderFormatting
u16 : U16 -> Encoder fmt | fmt has EncoderFormatting u16 : U16 -> Encoder fmt where fmt implements EncoderFormatting
u32 : U32 -> Encoder fmt | fmt has EncoderFormatting u32 : U32 -> Encoder fmt where fmt implements EncoderFormatting
u64 : U64 -> Encoder fmt | fmt has EncoderFormatting u64 : U64 -> Encoder fmt where fmt implements EncoderFormatting
u128 : U128 -> Encoder fmt | fmt has EncoderFormatting u128 : U128 -> Encoder fmt where fmt implements EncoderFormatting
i8 : I8 -> Encoder fmt | fmt has EncoderFormatting i8 : I8 -> Encoder fmt where fmt implements EncoderFormatting
i16 : I16 -> Encoder fmt | fmt has EncoderFormatting i16 : I16 -> Encoder fmt where fmt implements EncoderFormatting
i32 : I32 -> Encoder fmt | fmt has EncoderFormatting i32 : I32 -> Encoder fmt where fmt implements EncoderFormatting
i64 : I64 -> Encoder fmt | fmt has EncoderFormatting i64 : I64 -> Encoder fmt where fmt implements EncoderFormatting
i128 : I128 -> Encoder fmt | fmt has EncoderFormatting i128 : I128 -> Encoder fmt where fmt implements EncoderFormatting
f32 : F32 -> Encoder fmt | fmt has EncoderFormatting f32 : F32 -> Encoder fmt where fmt implements EncoderFormatting
f64 : F64 -> Encoder fmt | fmt has EncoderFormatting f64 : F64 -> Encoder fmt where fmt implements EncoderFormatting
dec : Dec -> Encoder fmt | fmt has EncoderFormatting dec : Dec -> Encoder fmt where fmt implements EncoderFormatting
bool : Bool -> Encoder fmt | fmt has EncoderFormatting bool : Bool -> Encoder fmt where fmt implements EncoderFormatting
string : Str -> Encoder fmt | fmt has EncoderFormatting string : Str -> Encoder fmt where fmt implements EncoderFormatting
list : List elem, (elem -> Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting list : List elem, (elem -> Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting
record : List { key : Str, value : Encoder fmt } -> Encoder fmt | fmt has EncoderFormatting record : List { key : Str, value : Encoder fmt } -> Encoder fmt where fmt implements EncoderFormatting
tuple : List (Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting tuple : List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting
tag : Str, List (Encoder fmt) -> Encoder fmt | fmt has EncoderFormatting tag : Str, List (Encoder fmt) -> Encoder fmt where fmt implements EncoderFormatting
custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
custom = \encoder -> @Encoder encoder custom = \encoder -> @Encoder encoder
appendWith : List U8, Encoder fmt, fmt -> List U8 | fmt has EncoderFormatting appendWith : List U8, Encoder fmt, fmt -> List U8 where fmt implements EncoderFormatting
appendWith = \lst, @Encoder doEncoding, fmt -> doEncoding lst fmt appendWith = \lst, @Encoder doEncoding, fmt -> doEncoding lst fmt
append : List U8, val, fmt -> List U8 | val has Encoding, fmt has EncoderFormatting append : List U8, val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
append = \lst, val, fmt -> appendWith lst (toEncoder val) fmt append = \lst, val, fmt -> appendWith lst (toEncoder val) fmt
toBytes : val, fmt -> List U8 | val has Encoding, fmt has EncoderFormatting toBytes : val, fmt -> List U8 where val implements Encoding, fmt implements EncoderFormatting
toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt

View file

@ -29,39 +29,39 @@ interface Hash
] ]
## A value that can hashed. ## A value that can hashed.
Hash has Hash implements
## Hashes a value into a [Hasher]. ## Hashes a value into a [Hasher].
## Note that [hash] does not produce a hash value itself; the hasher must be ## Note that [hash] does not produce a hash value itself; the hasher must be
## [complete]d in order to extract the hash value. ## [complete]d in order to extract the hash value.
hash : hasher, a -> hasher | a has Hash, hasher has Hasher hash : hasher, a -> hasher where a implements Hash, hasher implements Hasher
## Describes a hashing algorithm that is fed bytes and produces an integer hash. ## Describes a hashing algorithm that is fed bytes and produces an integer hash.
## ##
## The [Hasher] ability describes general-purpose hashers. It only allows ## The [Hasher] ability describes general-purpose hashers. It only allows
## emission of 64-bit unsigned integer hashes. It is not suitable for ## emission of 64-bit unsigned integer hashes. It is not suitable for
## cryptographically-secure hashing. ## cryptographically-secure hashing.
Hasher has Hasher implements
## Adds a list of bytes to the hasher. ## Adds a list of bytes to the hasher.
addBytes : a, List U8 -> a | a has Hasher addBytes : a, List U8 -> a where a implements Hasher
## Adds a single U8 to the hasher. ## Adds a single U8 to the hasher.
addU8 : a, U8 -> a | a has Hasher addU8 : a, U8 -> a where a implements Hasher
## Adds a single U16 to the hasher. ## Adds a single U16 to the hasher.
addU16 : a, U16 -> a | a has Hasher addU16 : a, U16 -> a where a implements Hasher
## Adds a single U32 to the hasher. ## Adds a single U32 to the hasher.
addU32 : a, U32 -> a | a has Hasher addU32 : a, U32 -> a where a implements Hasher
## Adds a single U64 to the hasher. ## Adds a single U64 to the hasher.
addU64 : a, U64 -> a | a has Hasher addU64 : a, U64 -> a where a implements Hasher
## Adds a single U128 to the hasher. ## Adds a single U128 to the hasher.
addU128 : a, U128 -> a | a has Hasher addU128 : a, U128 -> a where a implements Hasher
## Completes the hasher, extracting a hash value from its ## Completes the hasher, extracting a hash value from its
## accumulated hash state. ## accumulated hash state.
complete : a -> U64 | a has Hasher complete : a -> U64 where a implements Hasher
## Adds a string into a [Hasher] by hashing its UTF-8 bytes. ## Adds a string into a [Hasher] by hashing its UTF-8 bytes.
hashStrBytes = \hasher, s -> hashStrBytes = \hasher, s ->
@ -73,33 +73,33 @@ hashList = \hasher, lst ->
hash accumHasher elem hash accumHasher elem
## Adds a single [Bool] to a hasher. ## Adds a single [Bool] to a hasher.
hashBool : a, Bool -> a | a has Hasher hashBool : a, Bool -> a where a implements Hasher
hashBool = \hasher, b -> hashBool = \hasher, b ->
asU8 = if b then 1 else 0 asU8 = if b then 1 else 0
addU8 hasher asU8 addU8 hasher asU8
## Adds a single I8 to a hasher. ## Adds a single I8 to a hasher.
hashI8 : a, I8 -> a | a has Hasher hashI8 : a, I8 -> a where a implements Hasher
hashI8 = \hasher, n -> addU8 hasher (Num.toU8 n) hashI8 = \hasher, n -> addU8 hasher (Num.toU8 n)
## Adds a single I16 to a hasher. ## Adds a single I16 to a hasher.
hashI16 : a, I16 -> a | a has Hasher hashI16 : a, I16 -> a where a implements Hasher
hashI16 = \hasher, n -> addU16 hasher (Num.toU16 n) hashI16 = \hasher, n -> addU16 hasher (Num.toU16 n)
## Adds a single I32 to a hasher. ## Adds a single I32 to a hasher.
hashI32 : a, I32 -> a | a has Hasher hashI32 : a, I32 -> a where a implements Hasher
hashI32 = \hasher, n -> addU32 hasher (Num.toU32 n) hashI32 = \hasher, n -> addU32 hasher (Num.toU32 n)
## Adds a single I64 to a hasher. ## Adds a single I64 to a hasher.
hashI64 : a, I64 -> a | a has Hasher hashI64 : a, I64 -> a where a implements Hasher
hashI64 = \hasher, n -> addU64 hasher (Num.toU64 n) hashI64 = \hasher, n -> addU64 hasher (Num.toU64 n)
## Adds a single I128 to a hasher. ## Adds a single I128 to a hasher.
hashI128 : a, I128 -> a | a has Hasher hashI128 : a, I128 -> a where a implements Hasher
hashI128 = \hasher, n -> addU128 hasher (Num.toU128 n) hashI128 = \hasher, n -> addU128 hasher (Num.toU128 n)
## Adds a single Nat to a hasher. ## Adds a single Nat to a hasher.
hashNat : a, Nat -> a | a has Hasher hashNat : a, Nat -> a where a implements Hasher
hashNat = \hasher, n -> hashNat = \hasher, n ->
isPlatform32bit = isPlatform32bit =
x : Nat x : Nat
@ -117,7 +117,7 @@ hashNat = \hasher, n ->
i128OfDec : Dec -> I128 i128OfDec : Dec -> I128
## Adds a single [Dec] to a hasher. ## Adds a single [Dec] to a hasher.
hashDec : a, Dec -> a | a has Hasher hashDec : a, Dec -> a where a implements Hasher
hashDec = \hasher, n -> hashI128 hasher (i128OfDec n) hashDec = \hasher, n -> hashI128 hasher (i128OfDec n)
## Adds a container of [Hash]able elements to a [Hasher] by hashing each element. ## Adds a container of [Hash]able elements to a [Hasher] by hashing each element.

View file

@ -43,52 +43,52 @@ interface Inspect
KeyValWalker state collection key val : collection, state, (state, key, val -> state) -> state KeyValWalker state collection key val : collection, state, (state, key, val -> state) -> state
ElemWalker state collection elem : collection, state, (state, elem -> state) -> state ElemWalker state collection elem : collection, state, (state, elem -> state) -> state
InspectFormatter has InspectFormatter implements
init : {} -> f | f has InspectFormatter init : {} -> f where f implements InspectFormatter
tag : Str, List (Inspector f) -> Inspector f | f has InspectFormatter tag : Str, List (Inspector f) -> Inspector f where f implements InspectFormatter
tuple : List (Inspector f) -> Inspector f | f has InspectFormatter tuple : List (Inspector f) -> Inspector f where f implements InspectFormatter
record : List { key : Str, value : Inspector f } -> Inspector f | f has InspectFormatter record : List { key : Str, value : Inspector f } -> Inspector f where f implements InspectFormatter
bool : Bool -> Inspector f | f has InspectFormatter bool : Bool -> Inspector f where f implements InspectFormatter
str : Str -> Inspector f | f has InspectFormatter str : Str -> Inspector f where f implements InspectFormatter
list : list, ElemWalker state list elem, (elem -> Inspector f) -> Inspector f | f has InspectFormatter list : list, ElemWalker state list elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter
set : set, ElemWalker state set elem, (elem -> Inspector f) -> Inspector f | f has InspectFormatter set : set, ElemWalker state set elem, (elem -> Inspector f) -> Inspector f where f implements InspectFormatter
dict : dict, KeyValWalker state dict key value, (key -> Inspector f), (value -> Inspector f) -> Inspector f | f has InspectFormatter dict : dict, KeyValWalker state dict key value, (key -> Inspector f), (value -> Inspector f) -> Inspector f where f implements InspectFormatter
# Note opaque is used for both opaque types and functions. # Note opaque is used for both opaque types and functions.
# The auto deriver for functions probably could put the function type. # The auto deriver for functions probably could put the function type.
# For regular opaque types, I think we can use the type name, though that may lead to some reflection related issues that still need to be discussed. # For regular opaque types, I think we can use the type name, though that may lead to some reflection related issues that still need to be discussed.
# As a simple baseline, it can just use the exact words `opaque` and `function` for now. # As a simple baseline, it can just use the exact words `opaque` and `function` for now.
# In text, this would render as `<opaque>`, `<function>`, etc # In text, this would render as `<opaque>`, `<function>`, etc
opaque : Str -> Inspector f | f has InspectFormatter opaque : Str -> Inspector f where f implements InspectFormatter
u8 : U8 -> Inspector f | f has InspectFormatter u8 : U8 -> Inspector f where f implements InspectFormatter
i8 : I8 -> Inspector f | f has InspectFormatter i8 : I8 -> Inspector f where f implements InspectFormatter
u16 : U16 -> Inspector f | f has InspectFormatter u16 : U16 -> Inspector f where f implements InspectFormatter
i16 : I16 -> Inspector f | f has InspectFormatter i16 : I16 -> Inspector f where f implements InspectFormatter
u32 : U32 -> Inspector f | f has InspectFormatter u32 : U32 -> Inspector f where f implements InspectFormatter
i32 : I32 -> Inspector f | f has InspectFormatter i32 : I32 -> Inspector f where f implements InspectFormatter
u64 : U64 -> Inspector f | f has InspectFormatter u64 : U64 -> Inspector f where f implements InspectFormatter
i64 : I64 -> Inspector f | f has InspectFormatter i64 : I64 -> Inspector f where f implements InspectFormatter
u128 : U128 -> Inspector f | f has InspectFormatter u128 : U128 -> Inspector f where f implements InspectFormatter
i128 : I128 -> Inspector f | f has InspectFormatter i128 : I128 -> Inspector f where f implements InspectFormatter
f32 : F32 -> Inspector f | f has InspectFormatter f32 : F32 -> Inspector f where f implements InspectFormatter
f64 : F64 -> Inspector f | f has InspectFormatter f64 : F64 -> Inspector f where f implements InspectFormatter
dec : Dec -> Inspector f | f has InspectFormatter dec : Dec -> Inspector f where f implements InspectFormatter
Inspector f := f -> f | f has InspectFormatter Inspector f := f -> f where f implements InspectFormatter
custom : (f -> f) -> Inspector f | f has InspectFormatter custom : (f -> f) -> Inspector f where f implements InspectFormatter
custom = @Inspector custom = @Inspector
apply : Inspector f, f -> f | f has InspectFormatter apply : Inspector f, f -> f where f implements InspectFormatter
apply = \@Inspector fn, fmt -> fn fmt apply = \@Inspector fn, fmt -> fn fmt
Inspect has Inspect implements
toInspector : val -> Inspector f | val has Inspect, f has InspectFormatter toInspector : val -> Inspector f where val implements Inspect, f implements InspectFormatter
inspect : val -> f | val has Inspect, f has InspectFormatter inspect : val -> f where val implements Inspect, f implements InspectFormatter
inspect = \val -> inspect = \val ->
(@Inspector valFn) = toInspector val (@Inspector valFn) = toInspector val
valFn (init {}) valFn (init {})

View file

@ -422,7 +422,7 @@ join = \lists ->
List.walk lists (List.withCapacity totalLength) (\state, list -> List.concat state list) List.walk lists (List.withCapacity totalLength) (\state, list -> List.concat state list)
contains : List a, a -> Bool | a has Eq contains : List a, a -> Bool where a implements Eq
contains = \list, needle -> contains = \list, needle ->
List.any list (\x -> x == needle) List.any list (\x -> x == needle)
@ -1104,7 +1104,7 @@ intersperse = \list, sep ->
## is considered to "start with" an empty list. ## is considered to "start with" an empty list.
## ##
## If the first list is empty, this only returns `Bool.true` if the second list is empty. ## If the first list is empty, this only returns `Bool.true` if the second list is empty.
startsWith : List elem, List elem -> Bool | elem has Eq startsWith : List elem, List elem -> Bool where elem implements Eq
startsWith = \list, prefix -> startsWith = \list, prefix ->
# TODO once we have seamless slices, verify that this wouldn't # TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compareSublists # have better performance with a function like List.compareSublists
@ -1116,7 +1116,7 @@ startsWith = \list, prefix ->
## is considered to "end with" an empty list. ## is considered to "end with" an empty list.
## ##
## If the first list is empty, this only returns `Bool.true` if the second list is empty. ## If the first list is empty, this only returns `Bool.true` if the second list is empty.
endsWith : List elem, List elem -> Bool | elem has Eq endsWith : List elem, List elem -> Bool where elem implements Eq
endsWith = \list, suffix -> endsWith = \list, suffix ->
# TODO once we have seamless slices, verify that this wouldn't # TODO once we have seamless slices, verify that this wouldn't
# have better performance with a function like List.compareSublists # have better performance with a function like List.compareSublists
@ -1146,7 +1146,7 @@ split = \elements, userSplitIndex ->
## ``` ## ```
## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Z, Baz] } ## List.splitFirst [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo], after: [Bar, Z, Baz] }
## ``` ## ```
splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq splitFirst : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
splitFirst = \list, delimiter -> splitFirst = \list, delimiter ->
when List.findFirstIndex list (\elem -> elem == delimiter) is when List.findFirstIndex list (\elem -> elem == delimiter) is
Ok index -> Ok index ->
@ -1162,7 +1162,7 @@ splitFirst = \list, delimiter ->
## ``` ## ```
## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Z, Bar], after: [Baz] } ## List.splitLast [Foo, Z, Bar, Z, Baz] Z == Ok { before: [Foo, Z, Bar], after: [Baz] }
## ``` ## ```
splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] | elem has Eq splitLast : List elem, elem -> Result { before : List elem, after : List elem } [NotFound] where elem implements Eq
splitLast = \list, delimiter -> splitLast = \list, delimiter ->
when List.findLastIndex list (\elem -> elem == delimiter) is when List.findLastIndex list (\elem -> elem == delimiter) is
Ok index -> Ok index ->

View file

@ -29,8 +29,8 @@ interface Set
## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) ## Provides a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type))
## type which stores a collection of unique values, without any ordering ## type which stores a collection of unique values, without any ordering
Set k := Dict.Dict k {} | k has Hash & Eq Set k := Dict.Dict k {} where k implements Hash & Eq
has [ implements [
Eq { Eq {
isEq, isEq,
}, },
@ -39,7 +39,7 @@ Set k := Dict.Dict k {} | k has Hash & Eq
}, },
] ]
isEq : Set k, Set k -> Bool | k has Hash & Eq isEq : Set k, Set k -> Bool where k implements Hash & Eq
isEq = \xs, ys -> isEq = \xs, ys ->
if len xs != len ys then if len xs != len ys then
Bool.false Bool.false
@ -50,7 +50,7 @@ isEq = \xs, ys ->
else else
Break Bool.false Break Bool.false
hashSet : hasher, Set k -> hasher | k has Hash & Eq, hasher has Hasher hashSet : hasher, Set k -> hasher where k implements Hash & Eq, hasher implements Hasher
hashSet = \hasher, @Set inner -> Hash.hash hasher inner hashSet = \hasher, @Set inner -> Hash.hash hasher inner
## Creates a new empty `Set`. ## Creates a new empty `Set`.
@ -60,13 +60,13 @@ hashSet = \hasher, @Set inner -> Hash.hash hasher inner
## ##
## expect countValues == 0 ## expect countValues == 0
## ``` ## ```
empty : {} -> Set k | k has Hash & Eq empty : {} -> Set k where k implements Hash & Eq
empty = \{} -> @Set (Dict.empty {}) empty = \{} -> @Set (Dict.empty {})
## Return a dictionary with space allocated for a number of entries. This ## Return a dictionary with space allocated for a number of entries. This
## may provide a performance optimization if you know how many entries will be ## may provide a performance optimization if you know how many entries will be
## inserted. ## inserted.
withCapacity : Nat -> Set k | k has Hash & Eq withCapacity : Nat -> Set k where k implements Hash & Eq
withCapacity = \cap -> withCapacity = \cap ->
@Set (Dict.withCapacity cap) @Set (Dict.withCapacity cap)
@ -77,7 +77,7 @@ withCapacity = \cap ->
## ##
## expect countValues == 1 ## expect countValues == 1
## ``` ## ```
single : k -> Set k | k has Hash & Eq single : k -> Set k where k implements Hash & Eq
single = \key -> single = \key ->
Dict.single key {} |> @Set Dict.single key {} |> @Set
@ -93,7 +93,7 @@ single = \key ->
## ##
## expect countValues == 3 ## expect countValues == 3
## ``` ## ```
insert : Set k, k -> Set k | k has Hash & Eq insert : Set k, k -> Set k where k implements Hash & Eq
insert = \@Set dict, key -> insert = \@Set dict, key ->
Dict.insert dict key {} |> @Set Dict.insert dict key {} |> @Set
@ -178,7 +178,7 @@ expect
## expect has10 == Bool.false ## expect has10 == Bool.false
## expect has20 == Bool.true ## expect has20 == Bool.true
## ``` ## ```
remove : Set k, k -> Set k | k has Hash & Eq remove : Set k, k -> Set k where k implements Hash & Eq
remove = \@Set dict, key -> remove = \@Set dict, key ->
Dict.remove dict key |> @Set Dict.remove dict key |> @Set
@ -197,7 +197,7 @@ remove = \@Set dict, key ->
## expect hasApple == Bool.true ## expect hasApple == Bool.true
## expect hasBanana == Bool.false ## expect hasBanana == Bool.false
## ``` ## ```
contains : Set k, k -> Bool | k has Hash & Eq contains : Set k, k -> Bool where k implements Hash & Eq
contains = \@Set dict, key -> contains = \@Set dict, key ->
Dict.contains dict key Dict.contains dict key
@ -210,7 +210,7 @@ contains = \@Set dict, key ->
## ##
## expect Set.toList numbers == values ## expect Set.toList numbers == values
## ``` ## ```
toList : Set k -> List k | k has Hash & Eq toList : Set k -> List k where k implements Hash & Eq
toList = \@Set dict -> toList = \@Set dict ->
Dict.keys dict Dict.keys dict
@ -224,7 +224,7 @@ toList = \@Set dict ->
## ##
## expect Set.fromList [Pear, Apple, Banana] == values ## expect Set.fromList [Pear, Apple, Banana] == values
## ``` ## ```
fromList : List k -> Set k | k has Hash & Eq fromList : List k -> Set k where k implements Hash & Eq
fromList = \list -> fromList = \list ->
initial = @Set (Dict.withCapacity (List.len list)) initial = @Set (Dict.withCapacity (List.len list))
@ -240,7 +240,7 @@ fromList = \list ->
## ##
## expect Set.union set1 set2 == Set.fromList [Left, Right] ## expect Set.union set1 set2 == Set.fromList [Left, Right]
## ``` ## ```
union : Set k, Set k -> Set k | k has Hash & Eq union : Set k, Set k -> Set k where k implements Hash & Eq
union = \@Set dict1, @Set dict2 -> union = \@Set dict1, @Set dict2 ->
Dict.insertAll dict1 dict2 |> @Set Dict.insertAll dict1 dict2 |> @Set
@ -253,7 +253,7 @@ union = \@Set dict1, @Set dict2 ->
## ##
## expect Set.intersection set1 set2 == Set.single Left ## expect Set.intersection set1 set2 == Set.single Left
## ``` ## ```
intersection : Set k, Set k -> Set k | k has Hash & Eq intersection : Set k, Set k -> Set k where k implements Hash & Eq
intersection = \@Set dict1, @Set dict2 -> intersection = \@Set dict1, @Set dict2 ->
Dict.keepShared dict1 dict2 |> @Set Dict.keepShared dict1 dict2 |> @Set
@ -267,7 +267,7 @@ intersection = \@Set dict1, @Set dict2 ->
## ##
## expect Set.difference first second == Set.fromList [Up, Down] ## expect Set.difference first second == Set.fromList [Up, Down]
## ``` ## ```
difference : Set k, Set k -> Set k | k has Hash & Eq difference : Set k, Set k -> Set k where k implements Hash & Eq
difference = \@Set dict1, @Set dict2 -> difference = \@Set dict1, @Set dict2 ->
Dict.removeAll dict1 dict2 |> @Set Dict.removeAll dict1 dict2 |> @Set
@ -290,14 +290,14 @@ difference = \@Set dict1, @Set dict2 ->
## ##
## expect result == 2 ## expect result == 2
## ``` ## ```
walk : Set k, state, (state, k -> state) -> state | k has Hash & Eq walk : Set k, state, (state, k -> state) -> state where k implements Hash & Eq
walk = \@Set dict, state, step -> walk = \@Set dict, state, step ->
Dict.walk dict state (\s, k, _ -> step s k) Dict.walk dict state (\s, k, _ -> step s k)
## Convert each value in the set to something new, by calling a conversion ## Convert each value in the set to something new, by calling a conversion
## function on each of them which receives the old value. Then return a ## function on each of them which receives the old value. Then return a
## new set containing the converted values. ## new set containing the converted values.
map : Set a, (a -> b) -> Set b | a has Hash & Eq, b has Hash & Eq map : Set a, (a -> b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
map = \set, transform -> map = \set, transform ->
init = withCapacity (capacity set) init = withCapacity (capacity set)
@ -309,7 +309,7 @@ map = \set, transform ->
## (using [Set.union]) into one set. ## (using [Set.union]) into one set.
## ##
## You may know a similar function named `concatMap` in other languages. ## You may know a similar function named `concatMap` in other languages.
joinMap : Set a, (a -> Set b) -> Set b | a has Hash & Eq, b has Hash & Eq joinMap : Set a, (a -> Set b) -> Set b where a implements Hash & Eq, b implements Hash & Eq
joinMap = \set, transform -> joinMap = \set, transform ->
init = withCapacity (capacity set) # Might be a pessimization init = withCapacity (capacity set) # Might be a pessimization
@ -331,7 +331,7 @@ joinMap = \set, transform ->
## ##
## expect result == FoundTheAnswer ## expect result == FoundTheAnswer
## ``` ## ```
walkUntil : Set k, state, (state, k -> [Continue state, Break state]) -> state | k has Hash & Eq walkUntil : Set k, state, (state, k -> [Continue state, Break state]) -> state where k implements Hash & Eq
walkUntil = \@Set dict, state, step -> walkUntil = \@Set dict, state, step ->
Dict.walkUntil dict state (\s, k, _ -> step s k) Dict.walkUntil dict state (\s, k, _ -> step s k)

View file

@ -44,7 +44,7 @@ interface TotallyNotJson
## An opaque type with the `EncoderFormatting` and ## An opaque type with the `EncoderFormatting` and
## `DecoderFormatting` abilities. ## `DecoderFormatting` abilities.
Json := { fieldNameMapping : FieldNameMapping } Json := { fieldNameMapping : FieldNameMapping }
has [ implements [
EncoderFormatting { EncoderFormatting {
u8: encodeU8, u8: encodeU8,
u16: encodeU16, u16: encodeU16,

View file

@ -80,7 +80,7 @@ impl AbilityMemberData<Resolved> {
/// Solved lambda sets for an ability member specialization. For example, if we have /// Solved lambda sets for an ability member specialization. For example, if we have
/// ///
/// Default has default : {} -[[] + a:default:1]-> a | a has Default /// Default implements default : {} -[[] + a:default:1]-> a where a implements Default
/// ///
/// A := {} /// A := {}
/// default = \{} -[[closA]]-> @A {} /// default = \{} -[[closA]]-> @A {}
@ -144,7 +144,7 @@ pub struct IAbilitiesStore<Phase: ResolvePhase> {
/// ///
/// For example, in the program /// For example, in the program
/// ///
/// Hash has hash : a -> U64 | a has Hash /// Hash implements hash : a -> U64 where a implements Hash
/// ///
/// Id := {} implements [Hash {hash: myHash}] /// Id := {} implements [Hash {hash: myHash}]
/// myHash = \@Id n -> n /// myHash = \@Id n -> n
@ -155,7 +155,7 @@ pub struct IAbilitiesStore<Phase: ResolvePhase> {
/// Information about all members composing abilities. /// Information about all members composing abilities.
ability_members: MutMap<Symbol, AbilityMemberData<Phase>>, ability_members: MutMap<Symbol, AbilityMemberData<Phase>>,
/// Maps a tuple (member, type) specifying that `type` has an implementation of an ability /// Maps a tuple (member, type) specifying that `type` implements an ability
/// member `member`, to how that implementation is defined. /// member `member`, to how that implementation is defined.
declared_implementations: MutMap<ImplKey, MemberImpl>, declared_implementations: MutMap<ImplKey, MemberImpl>,
@ -284,7 +284,7 @@ impl<Phase: ResolvePhase> IAbilitiesStore<Phase> {
} }
/// Finds the implementation key for a symbol specializing the ability member, if it specializes any. /// Finds the implementation key for a symbol specializing the ability member, if it specializes any.
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 | a has Hash`. /// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 where a implements Hash`.
/// Calling this with `hashId` would retrieve (hash, hashId). /// Calling this with `hashId` would retrieve (hash, hashId).
pub fn impl_key(&self, specializing_symbol: Symbol) -> Option<&ImplKey> { pub fn impl_key(&self, specializing_symbol: Symbol) -> Option<&ImplKey> {
self.specialization_to_root.get(&specializing_symbol) self.specialization_to_root.get(&specializing_symbol)
@ -392,7 +392,7 @@ pub enum MarkError {
impl IAbilitiesStore<Resolved> { impl IAbilitiesStore<Resolved> {
/// Finds the symbol name and ability member definition for a symbol specializing the ability /// Finds the symbol name and ability member definition for a symbol specializing the ability
/// member, if it specializes any. /// member, if it specializes any.
/// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 | a has Hash`. /// For example, suppose `hashId : Id -> U64` specializes `hash : a -> U64 where a implements Hash`.
/// Calling this with `hashId` would retrieve the ability member data for `hash`, and what type /// Calling this with `hashId` would retrieve the ability member data for `hash`, and what type
/// `hashId` is specializing for. /// `hashId` is specializing for.
pub fn impl_key_and_def( pub fn impl_key_and_def(
@ -414,7 +414,7 @@ impl IAbilitiesStore<Resolved> {
} }
/// Returns an iterator over pairs ((ability member, type), implementation) specifying that /// Returns an iterator over pairs ((ability member, type), implementation) specifying that
/// the give type has an implementation of an ability member. /// the given type implements an ability member.
pub fn iter_declared_implementations( pub fn iter_declared_implementations(
&self, &self,
) -> impl Iterator<Item = (ImplKey, &MemberImpl)> + '_ { ) -> impl Iterator<Item = (ImplKey, &MemberImpl)> + '_ {

View file

@ -122,7 +122,7 @@ pub struct NamedVariable {
pub first_seen: Region, pub first_seen: Region,
} }
/// A type variable bound to an ability, like "a has Hash". /// A type variable bound to an ability, like "a implements Hash".
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct AbleVariable { pub struct AbleVariable {
pub variable: Variable, pub variable: Variable,
@ -296,7 +296,7 @@ pub(crate) fn canonicalize_annotation(
let (annotation, region) = match annotation { let (annotation, region) = match annotation {
TypeAnnotation::Where(annotation, clauses) => { TypeAnnotation::Where(annotation, clauses) => {
// Add each "has" clause. The association of a variable to an ability will be saved on // Add each "implements" clause. The association of a variable to an ability will be saved on
// `introduced_variables`, which we'll process later. // `introduced_variables`, which we'll process later.
for clause in clauses.iter() { for clause in clauses.iter() {
let opt_err = canonicalize_has_clause( let opt_err = canonicalize_has_clause(
@ -1029,8 +1029,8 @@ fn can_annotation_help(
Where(_annotation, clauses) => { Where(_annotation, clauses) => {
debug_assert!(!clauses.is_empty()); debug_assert!(!clauses.is_empty());
// Has clauses are allowed only on the top level of a signature, which we handle elsewhere. // Implements clauses are allowed only on the top level of a signature, which we handle elsewhere.
env.problem(roc_problem::can::Problem::IllegalHasClause { env.problem(roc_problem::can::Problem::IllegalImplementsClause {
region: Region::across_all(clauses.iter().map(|clause| &clause.region)), region: Region::across_all(clauses.iter().map(|clause| &clause.region)),
}); });
@ -1053,13 +1053,13 @@ fn canonicalize_has_clause(
scope: &mut Scope, scope: &mut Scope,
var_store: &mut VarStore, var_store: &mut VarStore,
introduced_variables: &mut IntroducedVariables, introduced_variables: &mut IntroducedVariables,
clause: &Loc<roc_parse::ast::HasClause<'_>>, clause: &Loc<roc_parse::ast::ImplementsClause<'_>>,
pending_abilities_in_scope: &PendingAbilitiesInScope, pending_abilities_in_scope: &PendingAbilitiesInScope,
references: &mut VecSet<Symbol>, references: &mut VecSet<Symbol>,
) -> Result<(), Type> { ) -> Result<(), Type> {
let Loc { let Loc {
region, region,
value: roc_parse::ast::HasClause { var, abilities }, value: roc_parse::ast::ImplementsClause { var, abilities },
} = clause; } = clause;
let region = *region; let region = *region;
@ -1085,13 +1085,13 @@ fn canonicalize_has_clause(
// or an ability that was imported from elsewhere // or an ability that was imported from elsewhere
&& !scope.abilities_store.is_ability(symbol) && !scope.abilities_store.is_ability(symbol)
{ {
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); env.problem(roc_problem::can::Problem::ImplementsClauseIsNotAbility { region });
return Err(Type::Error); return Err(Type::Error);
} }
symbol symbol
} }
_ => { _ => {
env.problem(roc_problem::can::Problem::HasClauseIsNotAbility { region }); env.problem(roc_problem::can::Problem::ImplementsClauseIsNotAbility { region });
return Err(Type::Error); return Err(Type::Error);
} }
}; };
@ -1100,7 +1100,7 @@ fn canonicalize_has_clause(
let already_seen = can_abilities.insert(ability); let already_seen = can_abilities.insert(ability);
if already_seen { if already_seen {
env.problem(roc_problem::can::Problem::DuplicateHasAbility { ability, region }); env.problem(roc_problem::can::Problem::DuplicateImplementsAbility { ability, region });
} }
} }

View file

@ -191,7 +191,7 @@ enum PendingTypeDef<'a> {
name: Loc<Symbol>, name: Loc<Symbol>,
vars: Vec<Loc<Lowercase>>, vars: Vec<Loc<Lowercase>>,
ann: &'a Loc<ast::TypeAnnotation<'a>>, ann: &'a Loc<ast::TypeAnnotation<'a>>,
derived: Option<&'a Loc<ast::HasAbilities<'a>>>, derived: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
}, },
Ability { Ability {
@ -497,7 +497,7 @@ fn canonicalize_claimed_ability_impl<'a>(
// //
// interface F imports [] exposes [] // interface F imports [] exposes []
// //
// Hello := {} has [Encoding.{ toEncoder }] // Hello := {} implements [Encoding.{ toEncoder }]
// //
// toEncoder = \@Hello {} -> ... // toEncoder = \@Hello {} -> ...
// //
@ -509,7 +509,7 @@ fn canonicalize_claimed_ability_impl<'a>(
// //
// interface F imports [Encoding.{ toEncoder }] exposes [] // interface F imports [Encoding.{ toEncoder }] exposes []
// //
// Hello := {} has [Encoding.{ toEncoder }] // Hello := {} implements [Encoding.{ toEncoder }]
// //
// toEncoder = \@Hello {} -> ... // toEncoder = \@Hello {} -> ...
// //
@ -527,9 +527,9 @@ fn canonicalize_claimed_ability_impl<'a>(
// definition symbol, for example when the ability is defined in the same // definition symbol, for example when the ability is defined in the same
// module as an implementer: // module as an implementer:
// //
// Eq has eq : a, a -> U64 | a has Eq // Eq implements eq : a, a -> U64 where a implements Eq
// //
// A := U8 has [Eq {eq}] // A := U8 implements [Eq {eq}]
// //
// So, do a final check that the implementation symbol is not resolved directly // So, do a final check that the implementation symbol is not resolved directly
// to the member. // to the member.
@ -689,7 +689,7 @@ fn canonicalize_opaque<'a>(
name_str: &'a str, name_str: &'a str,
ann: &'a Loc<ast::TypeAnnotation<'a>>, ann: &'a Loc<ast::TypeAnnotation<'a>>,
vars: &[Loc<Lowercase>], vars: &[Loc<Lowercase>],
has_abilities: Option<&'a Loc<ast::HasAbilities<'a>>>, has_abilities: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
) -> Result<CanonicalizedOpaque<'a>, ()> { ) -> Result<CanonicalizedOpaque<'a>, ()> {
let alias = canonicalize_alias( let alias = canonicalize_alias(
env, env,
@ -712,7 +712,7 @@ fn canonicalize_opaque<'a>(
for has_ability in has_abilities.items { for has_ability in has_abilities.items {
let region = has_ability.region; let region = has_ability.region;
let (ability, opt_impls) = match has_ability.value.extract_spaces().item { let (ability, opt_impls) = match has_ability.value.extract_spaces().item {
ast::HasAbility::HasAbility { ability, impls } => (ability, impls), ast::ImplementsAbility::ImplementsAbility { ability, impls } => (ability, impls),
_ => internal_error!("spaces not extracted"), _ => internal_error!("spaces not extracted"),
}; };
@ -766,8 +766,8 @@ fn canonicalize_opaque<'a>(
// Did the user claim this implementation for a specialization of a different // Did the user claim this implementation for a specialization of a different
// type? e.g. // type? e.g.
// //
// A has [Hash {hash: myHash}] // A implements [Hash {hash: myHash}]
// B has [Hash {hash: myHash}] // B implements [Hash {hash: myHash}]
// //
// If so, that's an error and we drop the impl for this opaque type. // If so, that's an error and we drop the impl for this opaque type.
let member_impl = match scope.abilities_store.impl_key(impl_symbol) { let member_impl = match scope.abilities_store.impl_key(impl_symbol) {
@ -1198,7 +1198,7 @@ fn canonicalize_type_defs<'a>(
Loc<Symbol>, Loc<Symbol>,
Vec<Loc<Lowercase>>, Vec<Loc<Lowercase>>,
&'a Loc<ast::TypeAnnotation<'a>>, &'a Loc<ast::TypeAnnotation<'a>>,
Option<&'a Loc<ast::HasAbilities<'a>>>, Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
), ),
Ability(Loc<Symbol>, Vec<PendingAbilityMember<'a>>), Ability(Loc<Symbol>, Vec<PendingAbilityMember<'a>>),
} }
@ -1404,7 +1404,7 @@ fn resolve_abilities(
[] => { [] => {
// There are no variables bound to the parent ability - then this member doesn't // There are no variables bound to the parent ability - then this member doesn't
// need to be a part of the ability. // need to be a part of the ability.
env.problem(Problem::AbilityMemberMissingHasClause { env.problem(Problem::AbilityMemberMissingImplementsClause {
member: member_sym, member: member_sym,
ability, ability,
region: member_name_region, region: member_name_region,
@ -1414,7 +1414,7 @@ fn resolve_abilities(
} }
[..] => { [..] => {
// There is more than one variable bound to the member signature, so something like // There is more than one variable bound to the member signature, so something like
// Eq has eq : a, b -> Bool | a has Eq, b has Eq // Eq implements eq : a, b -> Bool where a implements Eq, b implements Eq
// We have no way of telling what type implements a particular instance of Eq in // We have no way of telling what type implements a particular instance of Eq in
// this case (a or b?), so disallow it. // this case (a or b?), so disallow it.
let span_has_clauses = Region::across_all( let span_has_clauses = Region::across_all(
@ -1427,7 +1427,7 @@ fn resolve_abilities(
env.problem(Problem::AbilityMemberMultipleBoundVars { env.problem(Problem::AbilityMemberMultipleBoundVars {
member: member_sym, member: member_sym,
ability, ability,
span_has_clauses, span_implements_clauses: span_has_clauses,
bound_var_names, bound_var_names,
}); });
// Pretend the member isn't a part of the ability // Pretend the member isn't a part of the ability
@ -2558,7 +2558,7 @@ fn to_pending_alias_or_opaque<'a>(
name: &'a Loc<&'a str>, name: &'a Loc<&'a str>,
vars: &'a [Loc<ast::Pattern<'a>>], vars: &'a [Loc<ast::Pattern<'a>>],
ann: &'a Loc<ast::TypeAnnotation<'a>>, ann: &'a Loc<ast::TypeAnnotation<'a>>,
opt_derived: Option<&'a Loc<ast::HasAbilities<'a>>>, opt_derived: Option<&'a Loc<ast::ImplementsAbilities<'a>>>,
kind: AliasKind, kind: AliasKind,
) -> PendingTypeDef<'a> { ) -> PendingTypeDef<'a> {
let region = Region::span_across(&name.region, &ann.region); let region = Region::span_across(&name.region, &ann.region);
@ -2677,7 +2677,7 @@ fn to_pending_type_def<'a>(
Ability { Ability {
header: TypeHeader { name, vars }, header: TypeHeader { name, vars },
members, members,
loc_has: _, loc_implements: _,
} => { } => {
let name = match scope let name = match scope
.introduce_without_shadow_symbol(&Ident::from(name.value), name.region) .introduce_without_shadow_symbol(&Ident::from(name.value), name.region)

View file

@ -76,7 +76,7 @@ pub enum Pattern {
Underscore, Underscore,
/// An identifier that marks a specialization of an ability member. /// An identifier that marks a specialization of an ability member.
/// For example, given an ability member definition `hash : a -> U64 | a has Hash`, /// For example, given an ability member definition `hash : a -> U64 where a implements Hash`,
/// there may be the specialization `hash : Bool -> U64`. In this case we generate a /// there may be the specialization `hash : Bool -> U64`. In this case we generate a
/// new symbol for the specialized "hash" identifier. /// new symbol for the specialized "hash" identifier.
AbilityMemberSpecialization { AbilityMemberSpecialization {

View file

@ -33,7 +33,7 @@ pub struct Scope {
imports: Vec<(Ident, Symbol, Region)>, imports: Vec<(Ident, Symbol, Region)>,
/// Shadows of an ability member, for example a local specialization of `eq` for the ability /// Shadows of an ability member, for example a local specialization of `eq` for the ability
/// member `Eq has eq : a, a -> Bool | a has Eq` gets a shadow symbol it can use for its /// member `Eq implements eq : a, a -> Bool where a implements Eq` gets a shadow symbol it can use for its
/// implementation. /// implementation.
/// ///
/// Only one shadow of an ability member is permitted per scope. /// Only one shadow of an ability member is permitted per scope.

View file

@ -674,7 +674,7 @@ pub enum FoundSymbol {
Symbol(Symbol), Symbol(Symbol),
} }
/// Given an ability Foo has foo : ..., returns (T, foo1) if the symbol at the given region is a /// Given an ability Foo implements foo : ..., returns (T, foo1) if the symbol at the given region is a
/// symbol foo1 that specializes foo for T. Otherwise if the symbol is foo but the specialization /// symbol foo1 that specializes foo for T. Otherwise if the symbol is foo but the specialization
/// is unknown, (Foo, foo) is returned. Otherwise [None] is returned. /// is unknown, (Foo, foo) is returned. Otherwise [None] is returned.
pub fn find_symbol_at( pub fn find_symbol_at(

View file

@ -63,7 +63,7 @@ fn wrap_in_decode_custom_decode_with(
// Decode.decodeWith bytes inner_decoder fmt : DecodeResult val // Decode.decodeWith bytes inner_decoder fmt : DecodeResult val
let (decode_with_call, decode_with_result_var) = { let (decode_with_call, decode_with_result_var) = {
// Decode.decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting // Decode.decodeWith : List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
let decode_with_type = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH); let decode_with_type = env.import_builtin_symbol_var(Symbol::DECODE_DECODE_WITH);
// Decode.decodeWith : bytes, inner_decoder, fmt -> DecoderResult (List val) // Decode.decodeWith : bytes, inner_decoder, fmt -> DecoderResult (List val)
@ -80,7 +80,7 @@ fn wrap_in_decode_custom_decode_with(
)), )),
); );
// List U8, Decoder val fmt, fmt -> DecodeResult val | fmt has DecoderFormatting // List U8, Decoder val fmt, fmt -> DecodeResult val where fmt implements DecoderFormatting
// ~ bytes, Decoder (List elem) fmt, fmt -> DecoderResult (List val) // ~ bytes, Decoder (List elem) fmt, fmt -> DecoderResult (List val)
env.unify(decode_with_type, this_decode_with_fn_var); env.unify(decode_with_type, this_decode_with_fn_var);
@ -169,7 +169,7 @@ fn wrap_in_decode_custom_decode_with(
// Decode.custom \bytes, fmt -> Decode.decodeWith bytes inner_decoder fmt // Decode.custom \bytes, fmt -> Decode.decodeWith bytes inner_decoder fmt
let (decode_custom_call, decoder_var) = { let (decode_custom_call, decoder_var) = {
// (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting // (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
let decode_custom_type = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM); let decode_custom_type = env.import_builtin_symbol_var(Symbol::DECODE_CUSTOM);
// (List U8, fmt -> DecodeResult (List elem)) -> Decoder (List elem) fmt // (List U8, fmt -> DecodeResult (List elem)) -> Decoder (List elem) fmt
@ -185,7 +185,7 @@ fn wrap_in_decode_custom_decode_with(
)), )),
); );
// (List U8, fmt -> DecodeResult val) -> Decoder val fmt | fmt has DecoderFormatting // (List U8, fmt -> DecodeResult val) -> Decoder val fmt where fmt implements DecoderFormatting
// ~ (List U8, fmt -> DecodeResult (List elem)) -> Decoder (List elem) fmt // ~ (List U8, fmt -> DecodeResult (List elem)) -> Decoder (List elem) fmt
env.unify(decode_custom_type, this_decode_custom_fn_var); env.unify(decode_custom_type, this_decode_custom_fn_var);

View file

@ -15,7 +15,7 @@ use crate::util::Env;
pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) { pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable) {
// Build // Build
// //
// def_symbol : Decoder (List elem) fmt | elem has Decoding, fmt has DecoderFormatting // def_symbol : Decoder (List elem) fmt where elem implements Decoding, fmt implements DecoderFormatting
// def_symbol = Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt // def_symbol = Decode.custom \bytes, fmt -> Decode.decodeWith bytes (Decode.list Decode.decoder) fmt
// //
// NB: reduction to `Decode.list Decode.decoder` is not possible to the HRR. // NB: reduction to `Decode.list Decode.decoder` is not possible to the HRR.
@ -27,10 +27,10 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable
// List elem // List elem
let elem_var = env.subs.fresh_unnamed_flex_var(); let elem_var = env.subs.fresh_unnamed_flex_var();
// Decode.decoder : Decoder elem fmt | elem has Decoding, fmt has EncoderFormatting // Decode.decoder : Decoder elem fmt where elem implements Decoding, fmt implements EncoderFormatting
let (elem_decoder, elem_decoder_var) = { let (elem_decoder, elem_decoder_var) = {
// build `Decode.decoder : Decoder elem fmt` type // build `Decode.decoder : Decoder elem fmt` type
// Decoder val fmt | val has Decoding, fmt has EncoderFormatting // Decoder val fmt where val implements Decoding, fmt implements EncoderFormatting
let elem_decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER); let elem_decoder_var = env.import_builtin_symbol_var(Symbol::DECODE_DECODER);
// set val ~ elem // set val ~ elem
@ -52,7 +52,7 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable
}; };
// Build `Decode.list Decode.decoder` type // Build `Decode.list Decode.decoder` type
// Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting // Decoder val fmt -[uls]-> Decoder (List val) fmt where fmt implements DecoderFormatting
let decode_list_fn_var = env.import_builtin_symbol_var(Symbol::DECODE_LIST); let decode_list_fn_var = env.import_builtin_symbol_var(Symbol::DECODE_LIST);
// Decoder elem fmt -a-> b // Decoder elem fmt -a-> b
@ -68,7 +68,7 @@ pub(crate) fn decoder(env: &mut Env<'_>, _def_symbol: Symbol) -> (Expr, Variable
)), )),
); );
// Decoder val fmt -[uls]-> Decoder (List val) fmt | fmt has DecoderFormatting // Decoder val fmt -[uls]-> Decoder (List val) fmt where fmt implements DecoderFormatting
// ~ Decoder elem fmt -a -> b // ~ Decoder elem fmt -a -> b
env.unify(decode_list_fn_var, this_decode_list_fn_var); env.unify(decode_list_fn_var, this_decode_list_fn_var);

View file

@ -27,7 +27,7 @@ use super::wrap_in_decode_custom_decode_with;
/// we'd like to generate an impl like /// we'd like to generate an impl like
/// ///
/// ```roc /// ```roc
/// decoder : Decoder {first: a, second: b} fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting /// decoder : Decoder {first: a, second: b} fmt where a implements Decoding, b implements Decoding, fmt implements DecoderFormatting
/// decoder = /// decoder =
/// initialState : {f0: Result a [NoField], f1: Result b [NoField]} /// initialState : {f0: Result a [NoField], f1: Result b [NoField]}
/// initialState = {f0: Err NoField, f1: Err NoField} /// initialState = {f0: Err NoField, f1: Err NoField}

View file

@ -28,7 +28,7 @@ use super::wrap_in_decode_custom_decode_with;
/// we'd like to generate an impl like /// we'd like to generate an impl like
/// ///
/// ```roc /// ```roc
/// decoder : Decoder (a, b) fmt | a has Decoding, b has Decoding, fmt has DecoderFormatting /// decoder : Decoder (a, b) fmt where a implements Decoding, b implements Decoding, fmt implements DecoderFormatting
/// decoder = /// decoder =
/// initialState : {e0: Result a [NoElem], e1: Result b [NoElem]} /// initialState : {e0: Result a [NoElem], e1: Result b [NoElem]}
/// initialState = {e0: Err NoElem, e1: Err NoElem} /// initialState = {e0: Err NoElem, e1: Err NoElem}

View file

@ -121,7 +121,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
); );
// build `toEncoder elem` type // build `toEncoder elem` type
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting // val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER); let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
// elem -[clos]-> t1 // elem -[clos]-> t1
@ -136,11 +136,11 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
)), )),
); );
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting // val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ elem -[clos]-> t1 // ~ elem -[clos]-> t1
env.unify(to_encoder_fn_var, elem_to_encoder_fn_var); env.unify(to_encoder_fn_var, elem_to_encoder_fn_var);
// toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt | fmt has EncoderFormatting // toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, elem_to_encoder_fn_var); let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, elem_to_encoder_fn_var);
let to_encoder_fn = Box::new(( let to_encoder_fn = Box::new((
to_encoder_fn_var, to_encoder_fn_var,
@ -201,7 +201,7 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
}); });
// build `Encode.list lst (\elem -> Encode.toEncoder elem)` type // build `Encode.list lst (\elem -> Encode.toEncoder elem)` type
// List e, (e -> Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting // List e, (e -> Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let encode_list_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_LIST); let encode_list_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_LIST);
// List elem, to_elem_encoder_fn_var -[clos]-> t1 // List elem, to_elem_encoder_fn_var -[clos]-> t1
@ -218,11 +218,11 @@ fn to_encoder_list(env: &mut Env<'_>, fn_name: Symbol) -> (Expr, Variable) {
)), )),
); );
// List e, (e -> Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting // List e, (e -> Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ List elem, to_elem_encoder_fn_var -[clos]-> t1 // ~ List elem, to_elem_encoder_fn_var -[clos]-> t1
env.unify(encode_list_fn_var, this_encode_list_fn_var); env.unify(encode_list_fn_var, this_encode_list_fn_var);
// Encode.list : List elem, to_elem_encoder_fn_var -[clos]-> Encoder fmt | fmt has EncoderFormatting // Encode.list : List elem, to_elem_encoder_fn_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let encode_list = AbilityMember(Symbol::ENCODE_LIST, None, this_encode_list_fn_var); let encode_list = AbilityMember(Symbol::ENCODE_LIST, None, this_encode_list_fn_var);
let encode_list_fn = Box::new(( let encode_list_fn = Box::new((
this_encode_list_fn_var, this_encode_list_fn_var,
@ -340,7 +340,7 @@ fn to_encoder_record(
}; };
// build `toEncoder rcd.a` type // build `toEncoder rcd.a` type
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting // val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER); let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
// (typeof rcd.a) -[clos]-> t1 // (typeof rcd.a) -[clos]-> t1
@ -355,11 +355,11 @@ fn to_encoder_record(
)), )),
); );
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting // val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ (typeof rcd.a) -[clos]-> t1 // ~ (typeof rcd.a) -[clos]-> t1
env.unify(to_encoder_fn_var, this_to_encoder_fn_var); env.unify(to_encoder_fn_var, this_to_encoder_fn_var);
// toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt | fmt has EncoderFormatting // toEncoder : (typeof rcd.a) -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, to_encoder_fn_var); let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, to_encoder_fn_var);
let to_encoder_fn = Box::new(( let to_encoder_fn = Box::new((
to_encoder_fn_var, to_encoder_fn_var,
@ -420,7 +420,7 @@ fn to_encoder_record(
}; };
// build `Encode.record [ { key: .., value: ..}, .. ]` type // build `Encode.record [ { key: .., value: ..}, .. ]` type
// List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt | fmt has EncoderFormatting // List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let encode_record_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_RECORD); let encode_record_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_RECORD);
// fields_list_var -[clos]-> t1 // fields_list_var -[clos]-> t1
@ -437,11 +437,11 @@ fn to_encoder_record(
)), )),
); );
// List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt | fmt has EncoderFormatting // List { key : Str, value : Encoder fmt } -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ fields_list_var -[clos]-> t1 // ~ fields_list_var -[clos]-> t1
env.unify(encode_record_fn_var, this_encode_record_fn_var); env.unify(encode_record_fn_var, this_encode_record_fn_var);
// Encode.record : fields_list_var -[clos]-> Encoder fmt | fmt has EncoderFormatting // Encode.record : fields_list_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let encode_record_var = AbilityMember(Symbol::ENCODE_RECORD, None, encode_record_fn_var); let encode_record_var = AbilityMember(Symbol::ENCODE_RECORD, None, encode_record_fn_var);
let encode_record_fn = Box::new(( let encode_record_fn = Box::new((
encode_record_fn_var, encode_record_fn_var,
@ -543,7 +543,7 @@ fn to_encoder_tuple(
}; };
// build `toEncoder tup.0` type // build `toEncoder tup.0` type
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting // val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER); let to_encoder_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
// (typeof tup.0) -[clos]-> t1 // (typeof tup.0) -[clos]-> t1
@ -558,11 +558,11 @@ fn to_encoder_tuple(
)), )),
); );
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting // val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ (typeof tup.0) -[clos]-> t1 // ~ (typeof tup.0) -[clos]-> t1
env.unify(to_encoder_fn_var, this_to_encoder_fn_var); env.unify(to_encoder_fn_var, this_to_encoder_fn_var);
// toEncoder : (typeof tup.0) -[clos]-> Encoder fmt | fmt has EncoderFormatting // toEncoder : (typeof tup.0) -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, to_encoder_fn_var); let to_encoder_var = AbilityMember(Symbol::ENCODE_TO_ENCODER, None, to_encoder_fn_var);
let to_encoder_fn = Box::new(( let to_encoder_fn = Box::new((
to_encoder_fn_var, to_encoder_fn_var,
@ -603,7 +603,7 @@ fn to_encoder_tuple(
}; };
// build `Encode.tuple [ toEncoder tup.0, toEncoder tup.1 ]` type // build `Encode.tuple [ toEncoder tup.0, toEncoder tup.1 ]` type
// List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting // List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let encode_tuple_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TUPLE); let encode_tuple_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TUPLE);
// elem_encoders_list_var -[clos]-> t1 // elem_encoders_list_var -[clos]-> t1
@ -620,11 +620,11 @@ fn to_encoder_tuple(
)), )),
); );
// List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting // List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ elem_encoders_list_var -[clos]-> t1 // ~ elem_encoders_list_var -[clos]-> t1
env.unify(encode_tuple_fn_var, this_encode_tuple_fn_var); env.unify(encode_tuple_fn_var, this_encode_tuple_fn_var);
// Encode.tuple : elem_encoders_list_var -[clos]-> Encoder fmt | fmt has EncoderFormatting // Encode.tuple : elem_encoders_list_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let encode_tuple_var = AbilityMember(Symbol::ENCODE_TUPLE, None, encode_tuple_fn_var); let encode_tuple_var = AbilityMember(Symbol::ENCODE_TUPLE, None, encode_tuple_fn_var);
let encode_tuple_fn = Box::new(( let encode_tuple_fn = Box::new((
encode_tuple_fn_var, encode_tuple_fn_var,
@ -741,7 +741,7 @@ fn to_encoder_tag_union(
.zip(payload_vars.iter()) .zip(payload_vars.iter())
.map(|(&sym, &sym_var)| { .map(|(&sym, &sym_var)| {
// build `toEncoder v1` type // build `toEncoder v1` type
// expected: val -[uls]-> Encoder fmt | fmt has EncoderFormatting // expected: val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_fn_var = let to_encoder_fn_var =
env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER); env.import_builtin_symbol_var(Symbol::ENCODE_TO_ENCODER);
@ -759,11 +759,11 @@ fn to_encoder_tag_union(
)), )),
); );
// val -[uls]-> Encoder fmt | fmt has EncoderFormatting // val -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ t1 -[clos]-> t' // ~ t1 -[clos]-> t'
env.unify(to_encoder_fn_var, this_to_encoder_fn_var); env.unify(to_encoder_fn_var, this_to_encoder_fn_var);
// toEncoder : t1 -[clos]-> Encoder fmt | fmt has EncoderFormatting // toEncoder : t1 -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let to_encoder_var = let to_encoder_var =
AbilityMember(Symbol::ENCODE_TO_ENCODER, None, this_to_encoder_fn_var); AbilityMember(Symbol::ENCODE_TO_ENCODER, None, this_to_encoder_fn_var);
let to_encoder_fn = Box::new(( let to_encoder_fn = Box::new((
@ -802,7 +802,7 @@ fn to_encoder_tag_union(
}; };
// build `Encode.tag "A" [ ... ]` type // build `Encode.tag "A" [ ... ]` type
// expected: Str, List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting // expected: Str, List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
let encode_tag_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TAG); let encode_tag_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_TAG);
// wanted: Str, List whole_encoders_var -[clos]-> t' // wanted: Str, List whole_encoders_var -[clos]-> t'
@ -821,11 +821,11 @@ fn to_encoder_tag_union(
)), )),
); );
// Str, List (Encoder fmt) -[uls]-> Encoder fmt | fmt has EncoderFormatting // Str, List (Encoder fmt) -[uls]-> Encoder fmt where fmt implements EncoderFormatting
// ~ Str, List whole_encoders_var -[clos]-> t' // ~ Str, List whole_encoders_var -[clos]-> t'
env.unify(encode_tag_fn_var, this_encode_tag_fn_var); env.unify(encode_tag_fn_var, this_encode_tag_fn_var);
// Encode.tag : Str, List whole_encoders_var -[clos]-> Encoder fmt | fmt has EncoderFormatting // Encode.tag : Str, List whole_encoders_var -[clos]-> Encoder fmt where fmt implements EncoderFormatting
let encode_tag_var = AbilityMember(Symbol::ENCODE_TAG, None, this_encode_tag_fn_var); let encode_tag_var = AbilityMember(Symbol::ENCODE_TAG, None, this_encode_tag_fn_var);
let encode_tag_fn = Box::new(( let encode_tag_fn = Box::new((
this_encode_tag_fn_var, this_encode_tag_fn_var,
@ -954,15 +954,15 @@ fn wrap_in_encode_custom(
let bytes_sym = env.new_symbol("bytes"); let bytes_sym = env.new_symbol("bytes");
let bytes_var = Variable::LIST_U8; let bytes_var = Variable::LIST_U8;
// fmt: fmt | fmt has EncoderFormatting // fmt: fmt where fmt implements EncoderFormatting
let fmt_sym = env.new_symbol("fmt"); let fmt_sym = env.new_symbol("fmt");
let fmt_var = env.subs.fresh_unnamed_flex_var(); let fmt_var = env.subs.fresh_unnamed_flex_var();
// build `Encode.appendWith bytes encoder fmt` type // build `Encode.appendWith bytes encoder fmt` type
// expected: Encode.appendWith : List U8, Encoder fmt, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting // expected: Encode.appendWith : List U8, Encoder fmt, fmt -[appendWith]-> List U8 where fmt implements EncoderFormatting
let append_with_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_APPEND_WITH); let append_with_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_APPEND_WITH);
// wanted: Encode.appendWith : List U8, encoder_var, fmt -[clos]-> List U8 | fmt has EncoderFormatting // wanted: Encode.appendWith : List U8, encoder_var, fmt -[clos]-> List U8 where fmt implements EncoderFormatting
let this_append_with_args_var_slice = let this_append_with_args_var_slice =
VariableSubsSlice::insert_into_subs(env.subs, [Variable::LIST_U8, encoder_var, fmt_var]); VariableSubsSlice::insert_into_subs(env.subs, [Variable::LIST_U8, encoder_var, fmt_var]);
let this_append_with_clos_var = env.subs.fresh_unnamed_flex_var(); // -[clos]-> let this_append_with_clos_var = env.subs.fresh_unnamed_flex_var(); // -[clos]->
@ -975,11 +975,11 @@ fn wrap_in_encode_custom(
)), )),
); );
// List U8, Encoder fmt, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting // List U8, Encoder fmt, fmt -[appendWith]-> List U8 where fmt implements EncoderFormatting
// ~ List U8, encoder_var, fmt -[clos]-> List U8 | fmt has EncoderFormatting // ~ List U8, encoder_var, fmt -[clos]-> List U8 where fmt implements EncoderFormatting
env.unify(append_with_fn_var, this_append_with_fn_var); env.unify(append_with_fn_var, this_append_with_fn_var);
// Encode.appendWith : List U8, encoder_var, fmt -[appendWith]-> List U8 | fmt has EncoderFormatting // Encode.appendWith : List U8, encoder_var, fmt -[appendWith]-> List U8 where fmt implements EncoderFormatting
let append_with_fn = Box::new(( let append_with_fn = Box::new((
this_append_with_fn_var, this_append_with_fn_var,
Loc::at_zero(Var(Symbol::ENCODE_APPEND_WITH, this_append_with_fn_var)), Loc::at_zero(Var(Symbol::ENCODE_APPEND_WITH, this_append_with_fn_var)),
@ -1050,7 +1050,7 @@ fn wrap_in_encode_custom(
// Build // Build
// Encode.custom \bytes, fmt -> Encode.appendWith bytes encoder fmt // Encode.custom \bytes, fmt -> Encode.appendWith bytes encoder fmt
// //
// expected: Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting // expected: Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
let custom_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_CUSTOM); let custom_fn_var = env.import_builtin_symbol_var(Symbol::ENCODE_CUSTOM);
// wanted: Encode.custom : fn_var -[clos]-> t' // wanted: Encode.custom : fn_var -[clos]-> t'
@ -1066,11 +1066,11 @@ fn wrap_in_encode_custom(
)), )),
); );
// (List U8, fmt -> List U8) -[..]-> Encoder fmt | fmt has EncoderFormatting // (List U8, fmt -> List U8) -[..]-> Encoder fmt where fmt implements EncoderFormatting
// ~ fn_var -[clos]-> t' // ~ fn_var -[clos]-> t'
env.unify(custom_fn_var, this_custom_fn_var); env.unify(custom_fn_var, this_custom_fn_var);
// Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt | fmt has EncoderFormatting // Encode.custom : (List U8, fmt -> List U8) -> Encoder fmt where fmt implements EncoderFormatting
let custom_fn = Box::new(( let custom_fn = Box::new((
this_custom_fn_var, this_custom_fn_var,
Loc::at_zero(Var(Symbol::ENCODE_CUSTOM, this_custom_fn_var)), Loc::at_zero(Var(Symbol::ENCODE_CUSTOM, this_custom_fn_var)),

View file

@ -75,7 +75,7 @@ fn hash_record(env: &mut Env<'_>, fn_name: Symbol, fields: Vec<Lowercase>) -> (V
// Now, a hasher for this record is // Now, a hasher for this record is
// //
// hash_rcd : hasher, { f1: t1, ..., fn: tn } -> hasher | hasher has Hasher // hash_rcd : hasher, { f1: t1, ..., fn: tn } -> hasher where hasher implements Hasher
// hash_rcd = \hasher, rcd -> // hash_rcd = \hasher, rcd ->
// Hash.hash ( // Hash.hash (
// Hash.hash // Hash.hash
@ -144,7 +144,7 @@ fn hash_tuple(env: &mut Env<'_>, fn_name: Symbol, arity: u32) -> (Variable, Expr
// Now, a hasher for this tuple is // Now, a hasher for this tuple is
// //
// hash_tup : hasher, (t1, ..., tn) -> hasher | hasher has Hasher // hash_tup : hasher, (t1, ..., tn) -> hasher where hasher implements Hasher
// hash_tup = \hasher, tup -> // hash_tup = \hasher, tup ->
// Hash.hash ( // Hash.hash (
// Hash.hash // Hash.hash
@ -227,7 +227,7 @@ fn hash_tag_union(
// Now, a hasher for this tag union is // Now, a hasher for this tag union is
// //
// hash_union : hasher, [ A t11 .. t1n, ..., Q tq1 .. tqm ] -> hasher | hasher has Hasher // hash_union : hasher, [ A t11 .. t1n, ..., Q tq1 .. tqm ] -> hasher where hasher implements Hasher
// hash_union = \hasher, union -> // hash_union = \hasher, union ->
// when union is // when union is
// A x11 .. x1n -> Hash.hash (... (Hash.hash (Hash.uN hasher 0) x11) ...) x1n // A x11 .. x1n -> Hash.hash (... (Hash.hash (Hash.uN hasher 0) x11) ...) x1n
@ -393,7 +393,7 @@ fn hash_newtype_tag_union(
// Now, a hasher for this tag union is // Now, a hasher for this tag union is
// //
// hash_union : hasher, [ A t1 .. tn ] -> hasher | hasher has Hasher // hash_union : hasher, [ A t1 .. tn ] -> hasher where hasher implements Hasher
// hash_union = \hasher, A x1 .. xn -> // hash_union = \hasher, A x1 .. xn ->
// Hash.hash (... (Hash.hash discrHasher x1) ...) xn // Hash.hash (... (Hash.hash discrHasher x1) ...) xn
let hasher_sym = env.new_symbol("hasher"); let hasher_sym = env.new_symbol("hasher");
@ -462,7 +462,7 @@ fn call_hash_ability_member(
// build `member ...` function type. `member` here is `Hash.hash` or `Hash.addU16`. // build `member ...` function type. `member` here is `Hash.hash` or `Hash.addU16`.
// //
// hasher, val -[uls]-> hasher | hasher has Hasher, val has Hash // hasher, val -[uls]-> hasher where hasher implements Hasher, val implements Hash
let exposed_hash_fn_var = env.import_builtin_symbol_var(member); let exposed_hash_fn_var = env.import_builtin_symbol_var(member);
// (typeof body), (typeof field) -[clos]-> hasher_result // (typeof body), (typeof field) -[clos]-> hasher_result
@ -479,11 +479,11 @@ fn call_hash_ability_member(
)), )),
); );
// hasher, val -[uls]-> hasher | hasher has Hasher, val has Hash // hasher, val -[uls]-> hasher where hasher implements Hasher, val implements Hash
// ~ (typeof body), (typeof field) -[clos]-> hasher_result // ~ (typeof body), (typeof field) -[clos]-> hasher_result
env.unify(exposed_hash_fn_var, this_hash_fn_var); env.unify(exposed_hash_fn_var, this_hash_fn_var);
// Hash.hash : hasher, (typeof field) -[clos]-> hasher | hasher has Hasher, (typeof field) has Hash // Hash.hash : hasher, (typeof field) -[clos]-> hasher where hasher implements Hasher, (typeof field) implements Hash
let hash_fn_head = Expr::AbilityMember(member, None, this_hash_fn_var); let hash_fn_head = Expr::AbilityMember(member, None, this_hash_fn_var);
let hash_fn_data = Box::new(( let hash_fn_data = Box::new((
this_hash_fn_var, this_hash_fn_var,

View file

@ -146,7 +146,7 @@ impl Env<'_> {
}) })
.collect(); .collect();
// Since we're doing `{foo} ~ a | a has Encoding`, we may see "lambda sets to // Since we're doing `{foo} ~ a where a implements Encoding`, we may see "lambda sets to
// specialize" for e.g. `{foo}:toEncoder:1`, but these are actually just the // specialize" for e.g. `{foo}:toEncoder:1`, but these are actually just the
// specialization lambda sets, so we don't need to do any extra work! // specialization lambda sets, so we don't need to do any extra work!
// //

View file

@ -4,8 +4,8 @@ use crate::{
Buf, Buf,
}; };
use roc_parse::ast::{ use roc_parse::ast::{
AssignedField, Collection, Expr, ExtractSpaces, HasAbilities, HasAbility, HasClause, HasImpls, AbilityImpls, AssignedField, Collection, Expr, ExtractSpaces, ImplementsAbilities,
RecordBuilderField, Tag, TypeAnnotation, TypeHeader, ImplementsAbility, ImplementsClause, RecordBuilderField, Tag, TypeAnnotation, TypeHeader,
}; };
use roc_parse::ident::UppercaseIdent; use roc_parse::ident::UppercaseIdent;
use roc_region::all::Loc; use roc_region::all::Loc;
@ -350,16 +350,23 @@ impl<'a> Formattable for TypeAnnotation<'a> {
} }
} }
Where(annot, has_clauses) => { Where(annot, implements_clauses) => {
annot.format_with_options(buf, parens, newlines, indent); annot.format_with_options(buf, parens, newlines, indent);
if has_clauses.iter().any(|has| has.is_multiline()) { if implements_clauses
.iter()
.any(|implements| implements.is_multiline())
{
buf.newline(); buf.newline();
buf.indent(indent); buf.indent(indent);
} else { } else {
buf.spaces(1); buf.spaces(1);
} }
for (i, has) in has_clauses.iter().enumerate() { for (i, has) in implements_clauses.iter().enumerate() {
buf.push(if i == 0 { '|' } else { ',' }); buf.push_str(if i == 0 {
roc_parse::keyword::WHERE
} else {
","
});
buf.spaces(1); buf.spaces(1);
has.format_with_options(buf, parens, newlines, indent); has.format_with_options(buf, parens, newlines, indent);
} }
@ -645,16 +652,16 @@ impl<'a> Formattable for Tag<'a> {
} }
} }
impl<'a> Formattable for HasClause<'a> { impl<'a> Formattable for ImplementsClause<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
// No, always put abilities in a "has" clause on one line // No, always put abilities in an "implements" clause on one line
false false
} }
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) { fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
buf.push_str(self.var.value.extract_spaces().item); buf.push_str(self.var.value.extract_spaces().item);
buf.spaces(1); buf.spaces(1);
buf.push_str("has"); buf.push_str(roc_parse::keyword::IMPLEMENTS);
buf.spaces(1); buf.spaces(1);
for (i, ab) in self.abilities.iter().enumerate() { for (i, ab) in self.abilities.iter().enumerate() {
@ -668,30 +675,30 @@ impl<'a> Formattable for HasClause<'a> {
} }
} }
impl<'a> Formattable for HasImpls<'a> { impl<'a> Formattable for AbilityImpls<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
match self { match self {
HasImpls::SpaceBefore(_, _) | HasImpls::SpaceAfter(_, _) => true, AbilityImpls::SpaceBefore(_, _) | AbilityImpls::SpaceAfter(_, _) => true,
HasImpls::HasImpls(impls) => is_collection_multiline(impls), AbilityImpls::AbilityImpls(impls) => is_collection_multiline(impls),
} }
} }
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) { fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
match self { match self {
HasImpls::HasImpls(impls) => { AbilityImpls::AbilityImpls(impls) => {
if newlines == Newlines::Yes { if newlines == Newlines::Yes {
buf.newline(); buf.newline();
buf.indent(indent); buf.indent(indent);
} }
fmt_collection(buf, indent, Braces::Curly, *impls, Newlines::No); fmt_collection(buf, indent, Braces::Curly, *impls, Newlines::No);
} }
HasImpls::SpaceBefore(impls, spaces) => { AbilityImpls::SpaceBefore(impls, spaces) => {
buf.newline(); buf.newline();
buf.indent(indent); buf.indent(indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
impls.format_with_options(buf, parens, Newlines::No, indent); impls.format_with_options(buf, parens, Newlines::No, indent);
} }
HasImpls::SpaceAfter(impls, spaces) => { AbilityImpls::SpaceAfter(impls, spaces) => {
impls.format_with_options(buf, parens, newlines, indent); impls.format_with_options(buf, parens, newlines, indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
} }
@ -699,11 +706,11 @@ impl<'a> Formattable for HasImpls<'a> {
} }
} }
impl<'a> Formattable for HasAbility<'a> { impl<'a> Formattable for ImplementsAbility<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
match self { match self {
HasAbility::SpaceAfter(..) | HasAbility::SpaceBefore(..) => true, ImplementsAbility::SpaceAfter(..) | ImplementsAbility::SpaceBefore(..) => true,
HasAbility::HasAbility { ability, impls } => { ImplementsAbility::ImplementsAbility { ability, impls } => {
ability.is_multiline() || impls.map(|i| i.is_multiline()).unwrap_or(false) ability.is_multiline() || impls.map(|i| i.is_multiline()).unwrap_or(false)
} }
} }
@ -711,7 +718,7 @@ impl<'a> Formattable for HasAbility<'a> {
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) { fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
match self { match self {
HasAbility::HasAbility { ability, impls } => { ImplementsAbility::ImplementsAbility { ability, impls } => {
if newlines == Newlines::Yes { if newlines == Newlines::Yes {
buf.newline(); buf.newline();
buf.indent(indent); buf.indent(indent);
@ -722,13 +729,13 @@ impl<'a> Formattable for HasAbility<'a> {
impls.format_with_options(buf, parens, newlines, indent); impls.format_with_options(buf, parens, newlines, indent);
} }
} }
HasAbility::SpaceBefore(ab, spaces) => { ImplementsAbility::SpaceBefore(ab, spaces) => {
buf.newline(); buf.newline();
buf.indent(indent); buf.indent(indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
ab.format_with_options(buf, parens, Newlines::No, indent) ab.format_with_options(buf, parens, Newlines::No, indent)
} }
HasAbility::SpaceAfter(ab, spaces) => { ImplementsAbility::SpaceAfter(ab, spaces) => {
ab.format_with_options(buf, parens, newlines, indent); ab.format_with_options(buf, parens, newlines, indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
} }
@ -736,32 +743,34 @@ impl<'a> Formattable for HasAbility<'a> {
} }
} }
impl<'a> Formattable for HasAbilities<'a> { impl<'a> Formattable for ImplementsAbilities<'a> {
fn is_multiline(&self) -> bool { fn is_multiline(&self) -> bool {
match self { match self {
HasAbilities::SpaceAfter(..) | HasAbilities::SpaceBefore(..) => true, ImplementsAbilities::SpaceAfter(..) | ImplementsAbilities::SpaceBefore(..) => true,
HasAbilities::Has(has_abilities) => is_collection_multiline(has_abilities), ImplementsAbilities::Implements(has_abilities) => {
is_collection_multiline(has_abilities)
}
} }
} }
fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) { fn format_with_options(&self, buf: &mut Buf, parens: Parens, newlines: Newlines, indent: u16) {
match self { match self {
HasAbilities::Has(has_abilities) => { ImplementsAbilities::Implements(has_abilities) => {
if newlines == Newlines::Yes { if newlines == Newlines::Yes {
buf.newline(); buf.newline();
buf.indent(indent); buf.indent(indent);
} }
buf.push_str("has"); buf.push_str(roc_parse::keyword::IMPLEMENTS);
buf.spaces(1); buf.spaces(1);
fmt_collection(buf, indent, Braces::Square, *has_abilities, Newlines::No); fmt_collection(buf, indent, Braces::Square, *has_abilities, Newlines::No);
} }
HasAbilities::SpaceBefore(has_abilities, spaces) => { ImplementsAbilities::SpaceBefore(has_abilities, spaces) => {
buf.newline(); buf.newline();
buf.indent(indent); buf.indent(indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
has_abilities.format_with_options(buf, parens, Newlines::No, indent) has_abilities.format_with_options(buf, parens, Newlines::No, indent)
} }
HasAbilities::SpaceAfter(has_abilities, spaces) => { ImplementsAbilities::SpaceAfter(has_abilities, spaces) => {
has_abilities.format_with_options(buf, parens, newlines, indent); has_abilities.format_with_options(buf, parens, newlines, indent);
fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent); fmt_comments_only(buf, spaces.iter(), NewlineAt::Bottom, indent);
} }

View file

@ -123,7 +123,7 @@ impl<'a> Formattable for TypeDef<'a> {
} }
Ability { Ability {
header: TypeHeader { name, vars }, header: TypeHeader { name, vars },
loc_has: _, loc_implements: _,
members, members,
} => { } => {
buf.indent(indent); buf.indent(indent);
@ -133,8 +133,8 @@ impl<'a> Formattable for TypeDef<'a> {
fmt_pattern(buf, &var.value, indent, Parens::NotNeeded); fmt_pattern(buf, &var.value, indent, Parens::NotNeeded);
buf.indent(indent); buf.indent(indent);
} }
buf.spaces(1);
buf.push_str(" has"); buf.push_str(roc_parse::keyword::IMPLEMENTS);
if !self.is_multiline() { if !self.is_multiline() {
debug_assert_eq!(members.len(), 1); debug_assert_eq!(members.len(), 1);

View file

@ -3,10 +3,10 @@ use bumpalo::Bump;
use roc_module::called_via::{BinOp, UnaryOp}; use roc_module::called_via::{BinOp, UnaryOp};
use roc_parse::{ use roc_parse::{
ast::{ ast::{
AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr, Has, HasAbilities, AbilityImpls, AbilityMember, AssignedField, Collection, CommentOrNewline, Defs, Expr,
HasAbility, HasClause, HasImpls, Header, Module, Pattern, RecordBuilderField, Spaced, Header, Implements, ImplementsAbilities, ImplementsAbility, ImplementsClause, Module,
Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation, TypeDef, TypeHeader, ValueDef, Pattern, RecordBuilderField, Spaced, Spaces, StrLiteral, StrSegment, Tag, TypeAnnotation,
WhenBranch, TypeDef, TypeHeader, ValueDef, WhenBranch,
}, },
header::{ header::{
AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem, AppHeader, ExposedName, HostedHeader, ImportsEntry, InterfaceHeader, KeywordItem,
@ -507,14 +507,14 @@ impl<'a> RemoveSpaces<'a> for TypeDef<'a> {
}, },
Ability { Ability {
header: TypeHeader { name, vars }, header: TypeHeader { name, vars },
loc_has, loc_implements: loc_has,
members, members,
} => Ability { } => Ability {
header: TypeHeader { header: TypeHeader {
name: name.remove_spaces(arena), name: name.remove_spaces(arena),
vars: vars.remove_spaces(arena), vars: vars.remove_spaces(arena),
}, },
loc_has: loc_has.remove_spaces(arena), loc_implements: loc_has.remove_spaces(arena),
members: members.remove_spaces(arena), members: members.remove_spaces(arena),
}, },
} }
@ -569,9 +569,9 @@ impl<'a> RemoveSpaces<'a> for ValueDef<'a> {
} }
} }
impl<'a> RemoveSpaces<'a> for Has<'a> { impl<'a> RemoveSpaces<'a> for Implements<'a> {
fn remove_spaces(&self, _arena: &'a Bump) -> Self { fn remove_spaces(&self, _arena: &'a Bump) -> Self {
Has::Has Implements::Implements
} }
} }
@ -870,9 +870,9 @@ impl<'a> RemoveSpaces<'a> for TypeAnnotation<'a> {
} }
} }
impl<'a> RemoveSpaces<'a> for HasClause<'a> { impl<'a> RemoveSpaces<'a> for ImplementsClause<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
HasClause { ImplementsClause {
var: self.var.remove_spaces(arena), var: self.var.remove_spaces(arena),
abilities: self.abilities.remove_spaces(arena), abilities: self.abilities.remove_spaces(arena),
} }
@ -893,38 +893,43 @@ impl<'a> RemoveSpaces<'a> for Tag<'a> {
} }
} }
impl<'a> RemoveSpaces<'a> for HasImpls<'a> { impl<'a> RemoveSpaces<'a> for AbilityImpls<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self { match *self {
HasImpls::HasImpls(impls) => HasImpls::HasImpls(impls.remove_spaces(arena)), AbilityImpls::AbilityImpls(impls) => {
HasImpls::SpaceBefore(has, _) | HasImpls::SpaceAfter(has, _) => { AbilityImpls::AbilityImpls(impls.remove_spaces(arena))
}
AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => {
has.remove_spaces(arena) has.remove_spaces(arena)
} }
} }
} }
} }
impl<'a> RemoveSpaces<'a> for HasAbility<'a> { impl<'a> RemoveSpaces<'a> for ImplementsAbility<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self { match *self {
HasAbility::HasAbility { ability, impls } => HasAbility::HasAbility { ImplementsAbility::ImplementsAbility { ability, impls } => {
ImplementsAbility::ImplementsAbility {
ability: ability.remove_spaces(arena), ability: ability.remove_spaces(arena),
impls: impls.remove_spaces(arena), impls: impls.remove_spaces(arena),
}, }
HasAbility::SpaceBefore(has, _) | HasAbility::SpaceAfter(has, _) => { }
ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => {
has.remove_spaces(arena) has.remove_spaces(arena)
} }
} }
} }
} }
impl<'a> RemoveSpaces<'a> for HasAbilities<'a> { impl<'a> RemoveSpaces<'a> for ImplementsAbilities<'a> {
fn remove_spaces(&self, arena: &'a Bump) -> Self { fn remove_spaces(&self, arena: &'a Bump) -> Self {
match *self { match *self {
HasAbilities::Has(derived) => HasAbilities::Has(derived.remove_spaces(arena)), ImplementsAbilities::Implements(derived) => {
HasAbilities::SpaceBefore(derived, _) | HasAbilities::SpaceAfter(derived, _) => { ImplementsAbilities::Implements(derived.remove_spaces(arena))
derived.remove_spaces(arena) }
} ImplementsAbilities::SpaceBefore(derived, _)
| ImplementsAbilities::SpaceAfter(derived, _) => derived.remove_spaces(arena),
} }
} }
} }

View file

@ -1160,8 +1160,13 @@ impl<'a, 'r> WasmBackend<'a, 'r> {
*******************************************************************/ *******************************************************************/
fn expr_literal(&mut self, lit: &Literal<'a>, storage: &StoredValue) { fn expr_literal(&mut self, lit: &Literal<'a>, storage: &StoredValue) {
let invalid_error = let invalid_error = || {
|| internal_error!("Literal value {:?} has invalid storage {:?}", lit, storage); internal_error!(
"Literal value {:?} implements invalid storage {:?}",
lit,
storage
)
};
match storage { match storage {
StoredValue::VirtualMachineStack { value_type, .. } => { StoredValue::VirtualMachineStack { value_type, .. } => {

View file

@ -450,7 +450,7 @@ fn contains_unexposed_type(
false false
} }
Where(loc_ann, _loc_has_clauses) => { Where(loc_ann, _loc_has_clauses) => {
// We assume all the abilities in the `has` clause are from exported modules. // We assume all the abilities in the `implements` clause are from exported modules.
// TODO don't assume this! Instead, look them up and verify. // TODO don't assume this! Instead, look them up and verify.
contains_unexposed_type(&loc_ann.value, exposed_module_ids, module_ids) contains_unexposed_type(&loc_ann.value, exposed_module_ids, module_ids)
} }
@ -553,7 +553,7 @@ fn ability_member_type_to_docs(
let has_clauses = has_clauses let has_clauses = has_clauses
.iter() .iter()
.map(|hc| { .map(|hc| {
let ast::HasClause { var, abilities } = hc.value; let ast::ImplementsClause { var, abilities } = hc.value;
( (
var.value.extract_spaces().item.to_string(), var.value.extract_spaces().item.to_string(),
abilities abilities

View file

@ -2870,7 +2870,7 @@ fn update<'a>(
// # Default module // # Default module
// interface Default exposes [default, getDefault] // interface Default exposes [default, getDefault]
// //
// Default has default : {} -> a | a has Default // Default implements default : {} -> a where a implements Default
// //
// getDefault = \{} -> default {} // getDefault = \{} -> default {}
// //
@ -4287,7 +4287,7 @@ fn build_header<'a>(
// created an IdentId for this, when it was imported exposed // created an IdentId for this, when it was imported exposed
// in a dependent module. // in a dependent module.
// //
// For example, if module A has [B.{ foo }], then // For example, if module A implements [B.{ foo }], then
// when we get here for B, `foo` will already have // when we get here for B, `foo` will already have
// an IdentId. We must reuse that! // an IdentId. We must reuse that!
let ident_id = ident_ids.get_or_insert(loc_exposed.value.as_str()); let ident_id = ident_ids.get_or_insert(loc_exposed.value.as_str());
@ -4311,7 +4311,7 @@ fn build_header<'a>(
// created an IdentId for this, when it was imported exposed // created an IdentId for this, when it was imported exposed
// in a dependent module. // in a dependent module.
// //
// For example, if module A has [B.{ foo }], then // For example, if module A implements [B.{ foo }], then
// when we get here for B, `foo` will already have // when we get here for B, `foo` will already have
// an IdentId. We must reuse that! // an IdentId. We must reuse that!
let ident_id = ident_ids.get_or_insert(loc_name.value.as_str()); let ident_id = ident_ids.get_or_insert(loc_name.value.as_str());

View file

@ -13,7 +13,7 @@ Model position :
} }
initialModel : position -> Model position | position has Hash & Eq initialModel : position -> Model position where position implements Hash & Eq
initialModel = \start -> initialModel = \start ->
{ evaluated : Set.empty {} { evaluated : Set.empty {}
, openSet : Set.single start , openSet : Set.single start
@ -22,7 +22,7 @@ initialModel = \start ->
} }
cheapestOpen : (position -> F64), Model position -> Result position [KeyNotFound] | position has Hash & Eq cheapestOpen : (position -> F64), Model position -> Result position [KeyNotFound] where position implements Hash & Eq
cheapestOpen = \costFunction, model -> cheapestOpen = \costFunction, model ->
folder = \resSmallestSoFar, position -> folder = \resSmallestSoFar, position ->
@ -47,7 +47,7 @@ cheapestOpen = \costFunction, model ->
reconstructPath : Dict position position, position -> List position | position has Hash & Eq reconstructPath : Dict position position, position -> List position where position implements Hash & Eq
reconstructPath = \cameFrom, goal -> reconstructPath = \cameFrom, goal ->
when Dict.get cameFrom goal is when Dict.get cameFrom goal is
Err KeyNotFound -> Err KeyNotFound ->
@ -56,7 +56,7 @@ reconstructPath = \cameFrom, goal ->
Ok next -> Ok next ->
List.append (reconstructPath cameFrom next) goal List.append (reconstructPath cameFrom next) goal
updateCost : position, position, Model position -> Model position | position has Hash & Eq updateCost : position, position, Model position -> Model position where position implements Hash & Eq
updateCost = \current, neighbour, model -> updateCost = \current, neighbour, model ->
newCameFrom = Dict.insert model.cameFrom neighbour current newCameFrom = Dict.insert model.cameFrom neighbour current
@ -80,12 +80,12 @@ updateCost = \current, neighbour, model ->
model model
findPath : { costFunction: (position, position -> F64), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [KeyNotFound] | position has Hash & Eq findPath : { costFunction: (position, position -> F64), moveFunction: (position -> Set position), start : position, end : position } -> Result (List position) [KeyNotFound] where position implements Hash & Eq
findPath = \{ costFunction, moveFunction, start, end } -> findPath = \{ costFunction, moveFunction, start, end } ->
astar costFunction moveFunction end (initialModel start) astar costFunction moveFunction end (initialModel start)
astar : (position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] | position has Hash & Eq astar : (position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] where position implements Hash & Eq
astar = \costFn, moveFn, goal, model -> astar = \costFn, moveFn, goal, model ->
when cheapestOpen (\position -> costFn goal position) model is when cheapestOpen (\position -> costFn goal position) model is
Err _ -> Err _ ->

View file

@ -514,12 +514,12 @@ fn load_astar() {
expect_types( expect_types(
loaded_module, loaded_module,
hashmap! { hashmap! {
"findPath" => "{ costFunction : position, position -> F64, end : position, moveFunction : position -> Set position, start : position } -> Result (List position) [KeyNotFound] | position has Hash & Eq", "findPath" => "{ costFunction : position, position -> F64, end : position, moveFunction : position -> Set position, start : position } -> Result (List position) [KeyNotFound] where position implements Hash & Eq",
"initialModel" => "position -> Model position | position has Hash & Eq", "initialModel" => "position -> Model position where position implements Hash & Eq",
"reconstructPath" => "Dict position position, position -> List position | position has Hash & Eq", "reconstructPath" => "Dict position position, position -> List position where position implements Hash & Eq",
"updateCost" => "position, position, Model position -> Model position | position has Hash & Eq", "updateCost" => "position, position, Model position -> Model position where position implements Hash & Eq",
"cheapestOpen" => "(position -> F64), Model position -> Result position [KeyNotFound] | position has Hash & Eq", "cheapestOpen" => "(position -> F64), Model position -> Result position [KeyNotFound] where position implements Hash & Eq",
"astar" => "(position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] | position has Hash & Eq", "astar" => "(position, position -> F64), (position -> Set position), position, Model position -> [Err [KeyNotFound], Ok (List position)] where position implements Hash & Eq",
}, },
); );
} }

View file

@ -357,15 +357,15 @@ impl<'a> TypeHeader<'a> {
} }
} }
/// The `has` keyword associated with ability definitions. /// The `implements` keyword associated with ability definitions.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum Has<'a> { pub enum Implements<'a> {
Has, Implements,
SpaceBefore(&'a Has<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a Implements<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a Has<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a Implements<'a>, &'a [CommentOrNewline<'a>]),
} }
/// An ability demand is a value defining the ability; for example `hash : a -> U64 | a has Hash` /// An ability demand is a value defining the ability; for example `hash : a -> U64 where a implements Hash`
/// for a `Hash` ability. /// for a `Hash` ability.
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub struct AbilityMember<'a> { pub struct AbilityMember<'a> {
@ -394,15 +394,15 @@ pub enum TypeDef<'a> {
Opaque { Opaque {
header: TypeHeader<'a>, header: TypeHeader<'a>,
typ: Loc<TypeAnnotation<'a>>, typ: Loc<TypeAnnotation<'a>>,
derived: Option<Loc<HasAbilities<'a>>>, derived: Option<Loc<ImplementsAbilities<'a>>>,
}, },
/// An ability definition. E.g. /// An ability definition. E.g.
/// Hash has /// Hash implements
/// hash : a -> U64 | a has Hash /// hash : a -> U64 where a implements Hash
Ability { Ability {
header: TypeHeader<'a>, header: TypeHeader<'a>,
loc_has: Loc<Has<'a>>, loc_implements: Loc<Implements<'a>>,
members: &'a [AbilityMember<'a>], members: &'a [AbilityMember<'a>],
}, },
} }
@ -538,54 +538,54 @@ impl<'a> Defs<'a> {
pub type AbilityName<'a> = Loc<TypeAnnotation<'a>>; pub type AbilityName<'a> = Loc<TypeAnnotation<'a>>;
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub struct HasClause<'a> { pub struct ImplementsClause<'a> {
pub var: Loc<Spaced<'a, &'a str>>, pub var: Loc<Spaced<'a, &'a str>>,
pub abilities: &'a [AbilityName<'a>], pub abilities: &'a [AbilityName<'a>],
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum HasImpls<'a> { pub enum AbilityImpls<'a> {
// `{ eq: myEq }` // `{ eq: myEq }`
HasImpls(Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>), AbilityImpls(Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>),
// We preserve this for the formatter; canonicalization ignores it. // We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a HasImpls<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a AbilityImpls<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a HasImpls<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a AbilityImpls<'a>, &'a [CommentOrNewline<'a>]),
} }
/// `Eq` or `Eq { eq: myEq }` /// `Eq` or `Eq { eq: myEq }`
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum HasAbility<'a> { pub enum ImplementsAbility<'a> {
HasAbility { ImplementsAbility {
/// Should be a zero-argument `Apply` or an error; we'll check this in canonicalization /// Should be a zero-argument `Apply` or an error; we'll check this in canonicalization
ability: Loc<TypeAnnotation<'a>>, ability: Loc<TypeAnnotation<'a>>,
impls: Option<Loc<HasImpls<'a>>>, impls: Option<Loc<AbilityImpls<'a>>>,
}, },
// We preserve this for the formatter; canonicalization ignores it. // We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a HasAbility<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a ImplementsAbility<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a HasAbility<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a ImplementsAbility<'a>, &'a [CommentOrNewline<'a>]),
} }
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub enum HasAbilities<'a> { pub enum ImplementsAbilities<'a> {
/// `has [Eq { eq: myEq }, Hash]` /// `implements [Eq { eq: myEq }, Hash]`
Has(Collection<'a, Loc<HasAbility<'a>>>), Implements(Collection<'a, Loc<ImplementsAbility<'a>>>),
// We preserve this for the formatter; canonicalization ignores it. // We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a HasAbilities<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a ImplementsAbilities<'a>, &'a [CommentOrNewline<'a>]),
SpaceAfter(&'a HasAbilities<'a>, &'a [CommentOrNewline<'a>]), SpaceAfter(&'a ImplementsAbilities<'a>, &'a [CommentOrNewline<'a>]),
} }
impl HasAbilities<'_> { impl ImplementsAbilities<'_> {
pub fn collection(&self) -> &Collection<Loc<HasAbility>> { pub fn collection(&self) -> &Collection<Loc<ImplementsAbility>> {
let mut it = self; let mut it = self;
loop { loop {
match it { match it {
Self::SpaceBefore(inner, _) | Self::SpaceAfter(inner, _) => { Self::SpaceBefore(inner, _) | Self::SpaceAfter(inner, _) => {
it = inner; it = inner;
} }
Self::Has(collection) => return collection, Self::Implements(collection) => return collection,
} }
} }
} }
@ -641,8 +641,8 @@ pub enum TypeAnnotation<'a> {
/// The `*` type variable, e.g. in (List *) /// The `*` type variable, e.g. in (List *)
Wildcard, Wildcard,
/// A "where" clause demanding abilities designated by a `|`, e.g. `a -> U64 | a has Hash` /// A "where" clause demanding abilities designated by a `where`, e.g. `a -> U64 where a implements Hash`
Where(&'a Loc<TypeAnnotation<'a>>, &'a [Loc<HasClause<'a>>]), Where(&'a Loc<TypeAnnotation<'a>>, &'a [Loc<ImplementsClause<'a>>]),
// We preserve this for the formatter; canonicalization ignores it. // We preserve this for the formatter; canonicalization ignores it.
SpaceBefore(&'a TypeAnnotation<'a>, &'a [CommentOrNewline<'a>]), SpaceBefore(&'a TypeAnnotation<'a>, &'a [CommentOrNewline<'a>]),
@ -1245,39 +1245,39 @@ impl<'a> Spaceable<'a> for Tag<'a> {
} }
} }
impl<'a> Spaceable<'a> for Has<'a> { impl<'a> Spaceable<'a> for Implements<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
Has::SpaceBefore(self, spaces) Implements::SpaceBefore(self, spaces)
} }
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
Has::SpaceAfter(self, spaces) Implements::SpaceAfter(self, spaces)
} }
} }
impl<'a> Spaceable<'a> for HasImpls<'a> { impl<'a> Spaceable<'a> for AbilityImpls<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
HasImpls::SpaceBefore(self, spaces) AbilityImpls::SpaceBefore(self, spaces)
} }
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
HasImpls::SpaceAfter(self, spaces) AbilityImpls::SpaceAfter(self, spaces)
} }
} }
impl<'a> Spaceable<'a> for HasAbility<'a> { impl<'a> Spaceable<'a> for ImplementsAbility<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
HasAbility::SpaceBefore(self, spaces) ImplementsAbility::SpaceBefore(self, spaces)
} }
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
HasAbility::SpaceAfter(self, spaces) ImplementsAbility::SpaceAfter(self, spaces)
} }
} }
impl<'a> Spaceable<'a> for HasAbilities<'a> { impl<'a> Spaceable<'a> for ImplementsAbilities<'a> {
fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn before(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
HasAbilities::SpaceBefore(self, spaces) ImplementsAbilities::SpaceBefore(self, spaces)
} }
fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self { fn after(&'a self, spaces: &'a [CommentOrNewline<'a>]) -> Self {
HasAbilities::SpaceAfter(self, spaces) ImplementsAbilities::SpaceAfter(self, spaces)
} }
} }
@ -1368,7 +1368,7 @@ impl_extract_spaces!(Pattern);
impl_extract_spaces!(Tag); impl_extract_spaces!(Tag);
impl_extract_spaces!(AssignedField<T>); impl_extract_spaces!(AssignedField<T>);
impl_extract_spaces!(TypeAnnotation); impl_extract_spaces!(TypeAnnotation);
impl_extract_spaces!(HasAbility); impl_extract_spaces!(ImplementsAbility);
impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> { impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
type Item = T; type Item = T;
@ -1422,43 +1422,43 @@ impl<'a, T: Copy> ExtractSpaces<'a> for Spaced<'a, T> {
} }
} }
impl<'a> ExtractSpaces<'a> for HasImpls<'a> { impl<'a> ExtractSpaces<'a> for AbilityImpls<'a> {
type Item = Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>; type Item = Collection<'a, Loc<AssignedField<'a, Expr<'a>>>>;
fn extract_spaces(&self) -> Spaces<'a, Self::Item> { fn extract_spaces(&self) -> Spaces<'a, Self::Item> {
match self { match self {
HasImpls::HasImpls(inner) => Spaces { AbilityImpls::AbilityImpls(inner) => Spaces {
before: &[], before: &[],
item: *inner, item: *inner,
after: &[], after: &[],
}, },
HasImpls::SpaceBefore(item, before) => match item { AbilityImpls::SpaceBefore(item, before) => match item {
HasImpls::HasImpls(inner) => Spaces { AbilityImpls::AbilityImpls(inner) => Spaces {
before, before,
item: *inner, item: *inner,
after: &[], after: &[],
}, },
HasImpls::SpaceBefore(_, _) => todo!(), AbilityImpls::SpaceBefore(_, _) => todo!(),
HasImpls::SpaceAfter(HasImpls::HasImpls(inner), after) => Spaces { AbilityImpls::SpaceAfter(AbilityImpls::AbilityImpls(inner), after) => Spaces {
before, before,
item: *inner, item: *inner,
after, after,
}, },
HasImpls::SpaceAfter(_, _) => todo!(), AbilityImpls::SpaceAfter(_, _) => todo!(),
}, },
HasImpls::SpaceAfter(item, after) => match item { AbilityImpls::SpaceAfter(item, after) => match item {
HasImpls::HasImpls(inner) => Spaces { AbilityImpls::AbilityImpls(inner) => Spaces {
before: &[], before: &[],
item: *inner, item: *inner,
after, after,
}, },
HasImpls::SpaceBefore(HasImpls::HasImpls(inner), before) => Spaces { AbilityImpls::SpaceBefore(AbilityImpls::AbilityImpls(inner), before) => Spaces {
before, before,
item: *inner, item: *inner,
after, after,
}, },
HasImpls::SpaceBefore(_, _) => todo!(), AbilityImpls::SpaceBefore(_, _) => todo!(),
HasImpls::SpaceAfter(_, _) => todo!(), AbilityImpls::SpaceAfter(_, _) => todo!(),
}, },
} }
} }
@ -1681,11 +1681,11 @@ impl<'a> Malformed for TypeDef<'a> {
} => header.is_malformed() || typ.is_malformed() || derived.is_malformed(), } => header.is_malformed() || typ.is_malformed() || derived.is_malformed(),
TypeDef::Ability { TypeDef::Ability {
header, header,
loc_has, loc_implements,
members, members,
} => { } => {
header.is_malformed() header.is_malformed()
|| loc_has.is_malformed() || loc_implements.is_malformed()
|| members.iter().any(|member| member.is_malformed()) || members.iter().any(|member| member.is_malformed())
} }
} }
@ -1698,42 +1698,48 @@ impl<'a> Malformed for AbilityMember<'a> {
} }
} }
impl<'a> Malformed for Has<'a> { impl<'a> Malformed for Implements<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
match self { match self {
Has::Has => false, Implements::Implements => false,
Has::SpaceBefore(has, _) | Has::SpaceAfter(has, _) => has.is_malformed(), Implements::SpaceBefore(has, _) | Implements::SpaceAfter(has, _) => has.is_malformed(),
} }
} }
} }
impl<'a> Malformed for HasAbility<'a> { impl<'a> Malformed for ImplementsAbility<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
match self { match self {
HasAbility::HasAbility { ability, impls } => { ImplementsAbility::ImplementsAbility { ability, impls } => {
ability.is_malformed() || impls.iter().any(|impl_| impl_.is_malformed()) ability.is_malformed() || impls.iter().any(|impl_| impl_.is_malformed())
} }
HasAbility::SpaceBefore(has, _) | HasAbility::SpaceAfter(has, _) => has.is_malformed(), ImplementsAbility::SpaceBefore(has, _) | ImplementsAbility::SpaceAfter(has, _) => {
}
}
}
impl<'a> Malformed for HasAbilities<'a> {
fn is_malformed(&self) -> bool {
match self {
HasAbilities::Has(abilities) => abilities.iter().any(|ability| ability.is_malformed()),
HasAbilities::SpaceBefore(has, _) | HasAbilities::SpaceAfter(has, _) => {
has.is_malformed() has.is_malformed()
} }
} }
} }
} }
impl<'a> Malformed for HasImpls<'a> { impl<'a> Malformed for ImplementsAbilities<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
match self { match self {
HasImpls::HasImpls(impls) => impls.iter().any(|ability| ability.is_malformed()), ImplementsAbilities::Implements(abilities) => {
HasImpls::SpaceBefore(has, _) | HasImpls::SpaceAfter(has, _) => has.is_malformed(), abilities.iter().any(|ability| ability.is_malformed())
}
ImplementsAbilities::SpaceBefore(has, _) | ImplementsAbilities::SpaceAfter(has, _) => {
has.is_malformed()
}
}
}
}
impl<'a> Malformed for AbilityImpls<'a> {
fn is_malformed(&self) -> bool {
match self {
AbilityImpls::AbilityImpls(impls) => impls.iter().any(|ability| ability.is_malformed()),
AbilityImpls::SpaceBefore(has, _) | AbilityImpls::SpaceAfter(has, _) => {
has.is_malformed()
}
} }
} }
} }
@ -1823,7 +1829,7 @@ impl<'a> Malformed for Tag<'a> {
} }
} }
impl<'a> Malformed for HasClause<'a> { impl<'a> Malformed for ImplementsClause<'a> {
fn is_malformed(&self) -> bool { fn is_malformed(&self) -> bool {
self.abilities.iter().any(|ability| ability.is_malformed()) self.abilities.iter().any(|ability| ability.is_malformed())
} }

View file

@ -1,6 +1,7 @@
use crate::ast::{ use crate::ast::{
AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Has, HasAbilities, AssignedField, Collection, CommentOrNewline, Defs, Expr, ExtractSpaces, Implements,
Pattern, RecordBuilderField, Spaceable, Spaces, TypeAnnotation, TypeDef, TypeHeader, ValueDef, ImplementsAbilities, Pattern, RecordBuilderField, Spaceable, Spaces, TypeAnnotation, TypeDef,
TypeHeader, ValueDef,
}; };
use crate::blankspace::{ use crate::blankspace::{
space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e, space0_after_e, space0_around_e_no_after_indent_check, space0_around_ee, space0_before_e,
@ -14,7 +15,7 @@ use crate::parser::{
word2, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern, ERecord, EString, word2, EClosure, EExpect, EExpr, EIf, EInParens, EList, ENumber, EPattern, ERecord, EString,
EType, EWhen, Either, ParseResult, Parser, EType, EWhen, Either, ParseResult, Parser,
}; };
use crate::pattern::{closure_param, loc_has_parser}; use crate::pattern::{closure_param, loc_implements_parser};
use crate::state::State; use crate::state::State;
use crate::string_literal::StrLikeLiteral; use crate::string_literal::StrLikeLiteral;
use crate::type_annotation; use crate::type_annotation;
@ -616,14 +617,14 @@ pub fn parse_single_def<'a>(
}; };
if let Some((name, name_region, args)) = opt_tag_and_args { if let Some((name, name_region, args)) = opt_tag_and_args {
if let Ok((_, loc_has, state)) = if let Ok((_, loc_implements, state)) =
loc_has_parser().parse(arena, state.clone(), min_indent) loc_implements_parser().parse(arena, state.clone(), min_indent)
{ {
let (_, (type_def, def_region), state) = finish_parsing_ability_def_help( let (_, (type_def, def_region), state) = finish_parsing_ability_def_help(
min_indent, min_indent,
Loc::at(name_region, name), Loc::at(name_region, name),
args, args,
loc_has, loc_implements,
arena, arena,
state, state,
)?; )?;
@ -1063,8 +1064,14 @@ fn alias_signature_with_space_before<'a>() -> impl Parser<'a, Loc<TypeAnnotation
)) ))
} }
fn opaque_signature_with_space_before<'a>( fn opaque_signature_with_space_before<'a>() -> impl Parser<
) -> impl Parser<'a, (Loc<TypeAnnotation<'a>>, Option<Loc<HasAbilities<'a>>>), EExpr<'a>> { 'a,
(
Loc<TypeAnnotation<'a>>,
Option<Loc<ImplementsAbilities<'a>>>,
),
EExpr<'a>,
> {
and!( and!(
specialize( specialize(
EExpr::Type, EExpr::Type,
@ -1075,7 +1082,7 @@ fn opaque_signature_with_space_before<'a>(
), ),
optional(backtrackable(specialize( optional(backtrackable(specialize(
EExpr::Type, EExpr::Type,
space0_before_e(type_annotation::has_abilities(), EType::TIndentStart,), space0_before_e(type_annotation::implements_abilities(), EType::TIndentStart,),
))) )))
) )
} }
@ -1279,7 +1286,7 @@ mod ability {
Exact(u32), Exact(u32),
} }
/// Parses an ability demand like `hash : a -> U64 | a has Hash`, in the context of a larger /// Parses an ability demand like `hash : a -> U64 where a implements Hash`, in the context of a larger
/// ability definition. /// ability definition.
/// This is basically the same as parsing a free-floating annotation, but with stricter rules. /// This is basically the same as parsing a free-floating annotation, but with stricter rules.
pub fn parse_demand<'a>( pub fn parse_demand<'a>(
@ -1363,7 +1370,7 @@ fn finish_parsing_ability_def_help<'a>(
start_column: u32, start_column: u32,
name: Loc<&'a str>, name: Loc<&'a str>,
args: &'a [Loc<Pattern<'a>>], args: &'a [Loc<Pattern<'a>>],
loc_has: Loc<Has<'a>>, loc_implements: Loc<Implements<'a>>,
arena: &'a Bump, arena: &'a Bump,
state: State<'a>, state: State<'a>,
) -> ParseResult<'a, (TypeDef<'a>, Region), EExpr<'a>> { ) -> ParseResult<'a, (TypeDef<'a>, Region), EExpr<'a>> {
@ -1401,7 +1408,7 @@ fn finish_parsing_ability_def_help<'a>(
let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region); let def_region = Region::span_across(&name.region, &demands.last().unwrap().typ.region);
let type_def = TypeDef::Ability { let type_def = TypeDef::Ability {
header: TypeHeader { name, vars: args }, header: TypeHeader { name, vars: args },
loc_has, loc_implements,
members: demands.into_bump_slice(), members: demands.into_bump_slice(),
}; };
@ -1634,13 +1641,13 @@ fn parse_expr_end<'a>(
value: value:
Expr::Var { Expr::Var {
module_name: "", module_name: "",
ident: "has", ident: crate::keyword::IMPLEMENTS,
}, },
.. ..
}, },
state, state,
)) if matches!(expr_state.expr.value, Expr::Tag(..)) => { )) if matches!(expr_state.expr.value, Expr::Tag(..)) => {
// This is an ability definition, `Ability arg1 ... has ...`. // This is an ability definition, `Ability arg1 ... implements ...`.
let name = expr_state.expr.map_owned(|e| match e { let name = expr_state.expr.map_owned(|e| match e {
Expr::Tag(name) => name, Expr::Tag(name) => name,
@ -1661,13 +1668,13 @@ fn parse_expr_end<'a>(
} }
} }
// Attach any spaces to the `has` keyword // Attach any spaces to the `implements` keyword
let has = if !expr_state.spaces_after.is_empty() { let has = if !expr_state.spaces_after.is_empty() {
arena arena
.alloc(Has::Has) .alloc(Implements::Implements)
.with_spaces_before(expr_state.spaces_after, has.region) .with_spaces_before(expr_state.spaces_after, has.region)
} else { } else {
Loc::at(has.region, Has::Has) Loc::at(has.region, Implements::Implements)
}; };
let args = arguments.into_bump_slice(); let args = arguments.into_bump_slice();

View file

@ -1,3 +1,4 @@
// These keywords are valid in expressions
pub const IF: &str = "if"; pub const IF: &str = "if";
pub const THEN: &str = "then"; pub const THEN: &str = "then";
pub const ELSE: &str = "else"; pub const ELSE: &str = "else";
@ -9,4 +10,10 @@ pub const EXPECT: &str = "expect";
pub const EXPECT_FX: &str = "expect-fx"; pub const EXPECT_FX: &str = "expect-fx";
pub const CRASH: &str = "crash"; pub const CRASH: &str = "crash";
pub const KEYWORDS: [&str; 10] = [IF, THEN, ELSE, WHEN, AS, IS, DBG, EXPECT, EXPECT_FX, CRASH]; // These keywords are valid in types
pub const IMPLEMENTS: &str = "implements";
pub const WHERE: &str = "where";
pub const KEYWORDS: [&str; 11] = [
IF, THEN, ELSE, WHEN, AS, IS, DBG, EXPECT, EXPECT_FX, CRASH, WHERE,
];

View file

@ -599,7 +599,7 @@ pub enum EType<'a> {
TEnd(Position), TEnd(Position),
TFunctionArgument(Position), TFunctionArgument(Position),
TWhereBar(Position), TWhereBar(Position),
THasClause(Position), TImplementsClause(Position),
TAbilityImpl(ETypeAbilityImpl<'a>, Position), TAbilityImpl(ETypeAbilityImpl<'a>, Position),
/// ///
TIndentStart(Position), TIndentStart(Position),
@ -1524,6 +1524,23 @@ where
} }
} }
pub fn word<'a, ToError, E>(word: &'static str, to_error: ToError) -> impl Parser<'a, (), E>
where
ToError: Fn(Position) -> E,
E: 'a,
{
debug_assert!(!word.contains('\n'));
move |_arena: &'a Bump, state: State<'a>, _min_indent: u32| {
if state.bytes().starts_with(word.as_bytes()) {
let state = state.advance(word.len());
Ok((MadeProgress, (), state))
} else {
Err((NoProgress, to_error(state.pos())))
}
}
}
pub fn word1<'a, ToError, E>(word: u8, to_error: ToError) -> impl Parser<'a, (), E> pub fn word1<'a, ToError, E>(word: u8, to_error: ToError) -> impl Parser<'a, (), E>
where where
ToError: Fn(Position) -> E, ToError: Fn(Position) -> E,

View file

@ -1,4 +1,4 @@
use crate::ast::{Has, Pattern, PatternAs, Spaceable}; use crate::ast::{Implements, Pattern, PatternAs, Spaceable};
use crate::blankspace::{space0_e, spaces, spaces_before}; use crate::blankspace::{space0_e, spaces, spaces_before};
use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident}; use crate::ident::{lowercase_ident, parse_ident, Accessor, Ident};
use crate::keyword; use crate::keyword;
@ -116,7 +116,7 @@ fn loc_tag_pattern_args_help<'a>() -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>,
zero_or_more!(loc_tag_pattern_arg(false)) zero_or_more!(loc_tag_pattern_arg(false))
} }
/// Like `loc_tag_pattern_args_help`, but stops if a "has" keyword is seen (indicating an ability). /// Like `loc_tag_pattern_args_help`, but stops if a "implements" keyword is seen (indicating an ability).
fn loc_type_def_tag_pattern_args_help<'a>( fn loc_type_def_tag_pattern_args_help<'a>(
) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> { ) -> impl Parser<'a, Vec<'a, Loc<Pattern<'a>>>, EPattern<'a>> {
zero_or_more!(loc_tag_pattern_arg(true)) zero_or_more!(loc_tag_pattern_arg(true))
@ -138,7 +138,7 @@ fn loc_tag_pattern_arg<'a>(
let Loc { region, value } = loc_pat; let Loc { region, value } = loc_pat;
if stop_on_has_kw && matches!(value, Pattern::Identifier("has")) { if stop_on_has_kw && matches!(value, Pattern::Identifier(crate::keyword::IMPLEMENTS)) {
Err((NoProgress, EPattern::End(original_state.pos()))) Err((NoProgress, EPattern::End(original_state.pos())))
} else { } else {
Ok(( Ok((
@ -154,12 +154,19 @@ fn loc_tag_pattern_arg<'a>(
} }
} }
pub fn loc_has_parser<'a>() -> impl Parser<'a, Loc<Has<'a>>, EPattern<'a>> { pub fn loc_implements_parser<'a>() -> impl Parser<'a, Loc<Implements<'a>>, EPattern<'a>> {
then( then(
loc_tag_pattern_arg(false), loc_tag_pattern_arg(false),
|_arena, state, progress, pattern| { |_arena, state, progress, pattern| {
if matches!(pattern.value, Pattern::Identifier("has")) { if matches!(
Ok((progress, Loc::at(pattern.region, Has::Has), state)) pattern.value,
Pattern::Identifier(crate::keyword::IMPLEMENTS)
) {
Ok((
progress,
Loc::at(pattern.region, Implements::Implements),
state,
))
} else { } else {
Err((progress, EPattern::End(state.pos()))) Err((progress, EPattern::End(state.pos())))
} }

View file

@ -1,6 +1,6 @@
use crate::ast::{ use crate::ast::{
AssignedField, CommentOrNewline, Expr, HasAbilities, HasAbility, HasClause, HasImpls, Pattern, AbilityImpls, AssignedField, CommentOrNewline, Expr, ImplementsAbilities, ImplementsAbility,
Spaceable, Spaced, Tag, TypeAnnotation, TypeHeader, ImplementsClause, Pattern, Spaceable, Spaced, Tag, TypeAnnotation, TypeHeader,
}; };
use crate::blankspace::{ use crate::blankspace::{
space0_around_ee, space0_before_e, space0_before_optional_after, space0_e, space0_around_ee, space0_before_e, space0_before_optional_after, space0_e,
@ -12,7 +12,7 @@ use crate::parser::{
absolute_column_min_indent, increment_min_indent, then, ERecord, ETypeAbilityImpl, absolute_column_min_indent, increment_min_indent, then, ERecord, ETypeAbilityImpl,
}; };
use crate::parser::{ use crate::parser::{
allocated, backtrackable, fail, optional, specialize, specialize_ref, word1, word2, word3, allocated, backtrackable, fail, optional, specialize, specialize_ref, word, word1, word2,
EType, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, Parser, EType, ETypeApply, ETypeInParens, ETypeInlineAlias, ETypeRecord, ETypeTagUnion, Parser,
Progress::{self, *}, Progress::{self, *},
}; };
@ -426,7 +426,7 @@ fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, ETyp
EType::TIndentEnd, EType::TIndentEnd,
), ),
zero_or_more!(skip_first!( zero_or_more!(skip_first!(
word1(b'&', EType::THasClause), word1(b'&', EType::TImplementsClause),
space0_before_optional_after( space0_before_optional_after(
specialize(EType::TApply, loc!(concrete_type())), specialize(EType::TApply, loc!(concrete_type())),
EType::TIndentStart, EType::TIndentStart,
@ -444,9 +444,9 @@ fn ability_chain<'a>() -> impl Parser<'a, Vec<'a, Loc<TypeAnnotation<'a>>>, ETyp
) )
} }
fn has_clause<'a>() -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> { fn implements_clause<'a>() -> impl Parser<'a, Loc<ImplementsClause<'a>>, EType<'a>> {
map!( map!(
// Suppose we are trying to parse "a has Hash" // Suppose we are trying to parse "a implements Hash"
and!( and!(
space0_around_ee( space0_around_ee(
// Parse "a", with appropriate spaces // Parse "a", with appropriate spaces
@ -458,8 +458,8 @@ fn has_clause<'a>() -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
EType::TIndentEnd EType::TIndentEnd
), ),
skip_first!( skip_first!(
// Parse "has"; we don't care about this keyword // Parse "implements"; we don't care about this keyword
word3(b'h', b'a', b's', EType::THasClause), word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
// Parse "Hash & ..."; this may be qualified from another module like "Hash.Hash" // Parse "Hash & ..."; this may be qualified from another module like "Hash.Hash"
absolute_column_min_indent(ability_chain()) absolute_column_min_indent(ability_chain())
) )
@ -470,29 +470,33 @@ fn has_clause<'a>() -> impl Parser<'a, Loc<HasClause<'a>>, EType<'a>> {
&abilities.last().unwrap().region, &abilities.last().unwrap().region,
); );
let region = Region::span_across(&var.region, &abilities_region); let region = Region::span_across(&var.region, &abilities_region);
let has_clause = HasClause { let implements_clause = ImplementsClause {
var, var,
abilities: abilities.into_bump_slice(), abilities: abilities.into_bump_slice(),
}; };
Loc::at(region, has_clause) Loc::at(region, implements_clause)
} }
) )
} }
/// Parse a chain of `has` clauses, e.g. " | a has Hash, b has Eq". /// Parse a chain of `implements` clauses, e.g. " where a implements Hash, b implements Eq".
/// Returns the clauses and spaces before the starting "|", if there were any. /// Returns the clauses and spaces before the starting "where", if there were any.
fn has_clause_chain<'a>( fn implements_clause_chain<'a>(
) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<HasClause<'a>>]), EType<'a>> { ) -> impl Parser<'a, (&'a [CommentOrNewline<'a>], &'a [Loc<ImplementsClause<'a>>]), EType<'a>> {
move |arena, state: State<'a>, min_indent: u32| { move |arena, state: State<'a>, min_indent: u32| {
let (_, (spaces_before, ()), state) = let (_, (spaces_before, ()), state) = and!(
and!(space0_e(EType::TIndentStart), word1(b'|', EType::TWhereBar)) space0_e(EType::TIndentStart),
word(crate::keyword::WHERE, EType::TWhereBar)
)
.parse(arena, state, min_indent)?; .parse(arena, state, min_indent)?;
// Parse the first clause (there must be one), then the rest // Parse the first clause (there must be one), then the rest
let (_, first_clause, state) = has_clause().parse(arena, state, min_indent)?; let (_, first_clause, state) = implements_clause().parse(arena, state, min_indent)?;
let (_, mut clauses, state) = let (_, mut clauses, state) = zero_or_more!(skip_first!(
zero_or_more!(skip_first!(word1(b',', EType::THasClause), has_clause())) word1(b',', EType::TImplementsClause),
implements_clause()
))
.parse(arena, state, min_indent)?; .parse(arena, state, min_indent)?;
// Usually the number of clauses shouldn't be too large, so this is okay // Usually the number of clauses shouldn't be too large, so this is okay
@ -506,30 +510,30 @@ fn has_clause_chain<'a>(
} }
} }
/// Parse a has-abilities clause, e.g. `has [Eq, Hash]`. /// Parse a implements-abilities clause, e.g. `implements [Eq, Hash]`.
pub fn has_abilities<'a>() -> impl Parser<'a, Loc<HasAbilities<'a>>, EType<'a>> { pub fn implements_abilities<'a>() -> impl Parser<'a, Loc<ImplementsAbilities<'a>>, EType<'a>> {
increment_min_indent(skip_first!( increment_min_indent(skip_first!(
// Parse "has"; we don't care about this keyword // Parse "implements"; we don't care about this keyword
word3(b'h', b'a', b's', EType::THasClause), word(crate::keyword::IMPLEMENTS, EType::TImplementsClause),
// Parse "Hash"; this may be qualified from another module like "Hash.Hash" // Parse "Hash"; this may be qualified from another module like "Hash.Hash"
space0_before_e( space0_before_e(
loc!(map!( loc!(map!(
collection_trailing_sep_e!( collection_trailing_sep_e!(
word1(b'[', EType::TStart), word1(b'[', EType::TStart),
loc!(parse_has_ability()), loc!(parse_implements_ability()),
word1(b',', EType::TEnd), word1(b',', EType::TEnd),
word1(b']', EType::TEnd), word1(b']', EType::TEnd),
HasAbility::SpaceBefore ImplementsAbility::SpaceBefore
), ),
HasAbilities::Has ImplementsAbilities::Implements
)), )),
EType::TIndentEnd, EType::TIndentEnd,
) )
)) ))
} }
fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> { fn parse_implements_ability<'a>() -> impl Parser<'a, ImplementsAbility<'a>, EType<'a>> {
increment_min_indent(record!(HasAbility::HasAbility { increment_min_indent(record!(ImplementsAbility::ImplementsAbility {
ability: loc!(specialize(EType::TApply, concrete_type())), ability: loc!(specialize(EType::TApply, concrete_type())),
impls: optional(backtrackable(space0_before_e( impls: optional(backtrackable(space0_before_e(
loc!(map!( loc!(map!(
@ -543,7 +547,7 @@ fn parse_has_ability<'a>() -> impl Parser<'a, HasAbility<'a>, EType<'a>> {
AssignedField::SpaceBefore AssignedField::SpaceBefore
) )
), ),
HasImpls::HasImpls AbilityImpls::AbilityImpls
)), )),
EType::TIndentEnd EType::TIndentEnd
))) )))
@ -642,12 +646,13 @@ fn expression<'a>(
// Finally, try to parse a where clause if there is one. // Finally, try to parse a where clause if there is one.
// The where clause must be at least as deep as where the type annotation started. // The where clause must be at least as deep as where the type annotation started.
match has_clause_chain().parse(arena, state.clone(), min_indent) { match implements_clause_chain().parse(arena, state.clone(), min_indent) {
Ok((where_progress, (spaces_before, has_chain), state)) => { Ok((where_progress, (spaces_before, implements_chain), state)) => {
let region = Region::span_across(&annot.region, &has_chain.last().unwrap().region); let region =
Region::span_across(&annot.region, &implements_chain.last().unwrap().region);
let type_annot = if !spaces_before.is_empty() { let type_annot = if !spaces_before.is_empty() {
// We're transforming the spaces_before the '|' // We're transforming the spaces_before the 'where'
// into spaces_after the thing before the '|' // into spaces_after the thing before the 'where'
let spaced = arena let spaced = arena
.alloc(annot.value) .alloc(annot.value)
.with_spaces_after(spaces_before, annot.region); .with_spaces_after(spaces_before, annot.region);
@ -655,7 +660,7 @@ fn expression<'a>(
} else { } else {
&*arena.alloc(annot) &*arena.alloc(annot)
}; };
let where_annot = TypeAnnotation::Where(type_annot, has_chain); let where_annot = TypeAnnotation::Where(type_annot, implements_chain);
Ok(( Ok((
where_progress.or(progress), where_progress.or(progress),
Loc::at(region, where_annot), Loc::at(region, where_annot),
@ -724,7 +729,7 @@ fn parse_type_variable<'a>(
min_indent, min_indent,
) { ) {
Ok((_, name, state)) => { Ok((_, name, state)) => {
if name == "has" && stop_at_surface_has { if name == crate::keyword::IMPLEMENTS && stop_at_surface_has {
Err((NoProgress, EType::TEnd(state.pos()))) Err((NoProgress, EType::TEnd(state.pos())))
} else { } else {
let answer = TypeAnnotation::BoundVariable(name); let answer = TypeAnnotation::BoundVariable(name);

View file

@ -117,17 +117,17 @@ pub enum Problem {
name: Symbol, name: Symbol,
variables_region: Region, variables_region: Region,
}, },
HasClauseIsNotAbility { ImplementsClauseIsNotAbility {
region: Region, region: Region,
}, },
IllegalHasClause { IllegalImplementsClause {
region: Region, region: Region,
}, },
DuplicateHasAbility { DuplicateImplementsAbility {
ability: Symbol, ability: Symbol,
region: Region, region: Region,
}, },
AbilityMemberMissingHasClause { AbilityMemberMissingImplementsClause {
member: Symbol, member: Symbol,
ability: Symbol, ability: Symbol,
region: Region, region: Region,
@ -135,7 +135,7 @@ pub enum Problem {
AbilityMemberMultipleBoundVars { AbilityMemberMultipleBoundVars {
member: Symbol, member: Symbol,
ability: Symbol, ability: Symbol,
span_has_clauses: Region, span_implements_clauses: Region,
bound_var_names: Vec<Lowercase>, bound_var_names: Vec<Lowercase>,
}, },
AbilityNotOnToplevel { AbilityNotOnToplevel {
@ -245,10 +245,10 @@ impl Problem {
Problem::NestedDatatype { .. } => RuntimeError, Problem::NestedDatatype { .. } => RuntimeError,
Problem::InvalidExtensionType { .. } => RuntimeError, Problem::InvalidExtensionType { .. } => RuntimeError,
Problem::AbilityHasTypeVariables { .. } => RuntimeError, Problem::AbilityHasTypeVariables { .. } => RuntimeError,
Problem::HasClauseIsNotAbility { .. } => RuntimeError, Problem::ImplementsClauseIsNotAbility { .. } => RuntimeError,
Problem::IllegalHasClause { .. } => RuntimeError, Problem::IllegalImplementsClause { .. } => RuntimeError,
Problem::DuplicateHasAbility { .. } => Warning, Problem::DuplicateImplementsAbility { .. } => Warning,
Problem::AbilityMemberMissingHasClause { .. } => RuntimeError, Problem::AbilityMemberMissingImplementsClause { .. } => RuntimeError,
Problem::AbilityMemberMultipleBoundVars { .. } => RuntimeError, Problem::AbilityMemberMultipleBoundVars { .. } => RuntimeError,
Problem::AbilityNotOnToplevel { .. } => RuntimeError, // Ideally, could be compiled Problem::AbilityNotOnToplevel { .. } => RuntimeError, // Ideally, could be compiled
Problem::AbilityUsedAsType(_, _, _) => RuntimeError, Problem::AbilityUsedAsType(_, _, _) => RuntimeError,
@ -379,12 +379,12 @@ impl Problem {
variables_region: region, variables_region: region,
.. ..
} }
| Problem::HasClauseIsNotAbility { region } | Problem::ImplementsClauseIsNotAbility { region }
| Problem::IllegalHasClause { region } | Problem::IllegalImplementsClause { region }
| Problem::DuplicateHasAbility { region, .. } | Problem::DuplicateImplementsAbility { region, .. }
| Problem::AbilityMemberMissingHasClause { region, .. } | Problem::AbilityMemberMissingImplementsClause { region, .. }
| Problem::AbilityMemberMultipleBoundVars { | Problem::AbilityMemberMultipleBoundVars {
span_has_clauses: region, span_implements_clauses: region,
.. ..
} }
| Problem::AbilityNotOnToplevel { region } | Problem::AbilityNotOnToplevel { region }

View file

@ -741,7 +741,7 @@ trait DerivableVisitor {
) { ) {
// TODO: currently, just we suppose the presence of a flex var may // TODO: currently, just we suppose the presence of a flex var may
// include more or less things which we can derive. But, we should // include more or less things which we can derive. But, we should
// instead recurse here, and add a `t ~ u | u has Decode` constraint as needed. // instead recurse here, and add a `t ~ u where u implements Decode` constraint as needed.
stack.push(ext); stack.push(ext);
} }
} }

View file

@ -3146,7 +3146,7 @@ mod solve_expr {
Dict.insert Dict.insert
"# "#
), ),
"Dict k v, k, v -> Dict k v | k has Hash & Eq", "Dict k v, k, v -> Dict k v where k implements Hash & Eq",
); );
} }
@ -3407,7 +3407,7 @@ mod solve_expr {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
reconstructPath : Dict position position, position -> List position | position has Hash & Eq reconstructPath : Dict position position, position -> List position where position implements Hash & Eq
reconstructPath = \cameFrom, goal -> reconstructPath = \cameFrom, goal ->
when Dict.get cameFrom goal is when Dict.get cameFrom goal is
Err KeyNotFound -> Err KeyNotFound ->
@ -3419,7 +3419,7 @@ mod solve_expr {
reconstructPath reconstructPath
"# "#
), ),
"Dict position position, position -> List position | position has Hash & Eq", "Dict position position, position -> List position where position implements Hash & Eq",
); );
} }
@ -3454,7 +3454,7 @@ mod solve_expr {
Model position : { openSet : Set position } Model position : { openSet : Set position }
cheapestOpen : Model position -> Result position [KeyNotFound] | position has Hash & Eq cheapestOpen : Model position -> Result position [KeyNotFound] where position implements Hash & Eq
cheapestOpen = \model -> cheapestOpen = \model ->
folder = \resSmallestSoFar, position -> folder = \resSmallestSoFar, position ->
@ -3469,14 +3469,14 @@ mod solve_expr {
Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder Set.walk model.openSet (Ok { position: boom {}, cost: 0.0 }) folder
|> Result.map (\x -> x.position) |> Result.map (\x -> x.position)
astar : Model position -> Result position [KeyNotFound] | position has Hash & Eq astar : Model position -> Result position [KeyNotFound] where position implements Hash & Eq
astar = \model -> cheapestOpen model astar = \model -> cheapestOpen model
main = main =
astar astar
"# "#
), ),
"Model position -> Result position [KeyNotFound] | position has Hash & Eq", "Model position -> Result position [KeyNotFound] where position implements Hash & Eq",
); );
} }
@ -4118,7 +4118,7 @@ mod solve_expr {
Key k : Num k Key k : Num k
removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v where k implements Hash & Eq
removeHelpEQGT = \targetKey, dict -> removeHelpEQGT = \targetKey, dict ->
when dict is when dict is
Node color key value left right -> Node color key value left right ->
@ -4232,7 +4232,7 @@ mod solve_expr {
_ -> _ ->
Empty Empty
removeHelp : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq removeHelp : Key k, RBTree (Key k) v -> RBTree (Key k) v where k implements Hash & Eq
removeHelp = \targetKey, dict -> removeHelp = \targetKey, dict ->
when dict is when dict is
Empty -> Empty ->
@ -4320,7 +4320,7 @@ mod solve_expr {
RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty] RBTree k v : [Node NodeColor k v (RBTree k v) (RBTree k v), Empty]
removeHelp : Num k, RBTree (Num k) v -> RBTree (Num k) v | k has Hash & Eq removeHelp : Num k, RBTree (Num k) v -> RBTree (Num k) v where k implements Hash & Eq
removeHelp = \targetKey, dict -> removeHelp = \targetKey, dict ->
when dict is when dict is
Empty -> Empty ->
@ -4355,7 +4355,7 @@ mod solve_expr {
removeHelpPrepEQGT : Key k, RBTree (Key k) v, NodeColor, (Key k), v, RBTree (Key k) v, RBTree (Key k) v -> RBTree (Key k) v removeHelpPrepEQGT : Key k, RBTree (Key k) v, NodeColor, (Key k), v, RBTree (Key k) v, RBTree (Key k) v -> RBTree (Key k) v
removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v | k has Hash & Eq removeHelpEQGT : Key k, RBTree (Key k) v -> RBTree (Key k) v where k implements Hash & Eq
removeHelpEQGT = \targetKey, dict -> removeHelpEQGT = \targetKey, dict ->
when dict is when dict is
Node color key value left right -> Node color key value left right ->

View file

@ -87,7 +87,7 @@ fn derivable_record_ext_flex_var() {
fn derivable_record_ext_flex_able_var() { fn derivable_record_ext_flex_able_var() {
check_derivable( check_derivable(
Decoder, Decoder,
v!({ a: v!(STR), }a has Symbol::DECODE_DECODER ), v!({ a: v!(STR), }a implements Symbol::DECODE_DECODER ),
DeriveKey::Decoder(FlatDecodableKey::Record(vec!["a".into()])), DeriveKey::Decoder(FlatDecodableKey::Record(vec!["a".into()])),
); );
} }
@ -106,8 +106,8 @@ fn list() {
derive_test(Decoder, v!(Symbol::LIST_LIST v!(STR)), |golden| { derive_test(Decoder, v!(Symbol::LIST_LIST v!(STR)), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for List Str # derived for List Str
# Decoder (List val) fmt | fmt has DecoderFormatting, val has Decoding # Decoder (List val) fmt where fmt implements DecoderFormatting, val implements Decoding
# List U8, fmt -[[custom(3)]]-> { rest : List U8, result : [Err [TooShort], Ok (List val)] } | fmt has DecoderFormatting, val has Decoding # List U8, fmt -[[custom(3)]]-> { rest : List U8, result : [Err [TooShort], Ok (List val)] } where fmt implements DecoderFormatting, val implements Decoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[custom(3)]] # @<1>: [[custom(3)]]
#Derived.decoder_list = #Derived.decoder_list =
@ -124,8 +124,8 @@ fn record_2_fields() {
derive_test(Decoder, v!({first: v!(STR), second: v!(STR),}), |golden| { derive_test(Decoder, v!({first: v!(STR), second: v!(STR),}), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for { first : Str, second : Str } # derived for { first : Str, second : Str }
# Decoder { first : val, second : val1 } fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding # Decoder { first : val, second : val1 } fmt where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok { first : val, second : val1 }] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding # List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok { first : val, second : val1 }] } where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[custom(22)]] # @<1>: [[custom(22)]]
#Derived.decoder_{first,second} = #Derived.decoder_{first,second} =
@ -181,8 +181,8 @@ fn tuple_2_fields() {
derive_test(Decoder, v!((v!(STR), v!(U8),)), |golden| { derive_test(Decoder, v!((v!(STR), v!(U8),)), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for ( Str, U8 )* # derived for ( Str, U8 )*
# Decoder ( val, val1 )* fmt | fmt has DecoderFormatting, val has Decoding, val1 has Decoding # Decoder ( val, val1 )* fmt where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
# List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok ( val, val1 )a] } | fmt has DecoderFormatting, val has Decoding, val1 has Decoding # List U8, fmt -[[custom(22)]]-> { rest : List U8, result : [Err [TooShort], Ok ( val, val1 )a] } where fmt implements DecoderFormatting, val implements Decoding, val1 implements Decoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[custom(22)]] # @<1>: [[custom(22)]]
#Derived.decoder_(arity:2) = #Derived.decoder_(arity:2) =

View file

@ -139,7 +139,7 @@ fn derivable_record_ext_flex_var() {
fn derivable_record_ext_flex_able_var() { fn derivable_record_ext_flex_able_var() {
check_derivable( check_derivable(
ToEncoder, ToEncoder,
v!({ a: v!(STR), }a has Symbol::ENCODE_TO_ENCODER), v!({ a: v!(STR), }a implements Symbol::ENCODE_TO_ENCODER),
DeriveKey::ToEncoder(FlatEncodableKey::Record(vec!["a".into()])), DeriveKey::ToEncoder(FlatEncodableKey::Record(vec!["a".into()])),
); );
} }
@ -166,7 +166,7 @@ fn derivable_tag_ext_flex_var() {
fn derivable_tag_ext_flex_able_var() { fn derivable_tag_ext_flex_able_var() {
check_derivable( check_derivable(
ToEncoder, ToEncoder,
v!([ A v!(STR) ]a has Symbol::ENCODE_TO_ENCODER), v!([ A v!(STR) ]a implements Symbol::ENCODE_TO_ENCODER),
DeriveKey::ToEncoder(FlatEncodableKey::TagUnion(vec![("A".into(), 1)])), DeriveKey::ToEncoder(FlatEncodableKey::TagUnion(vec![("A".into(), 1)])),
); );
} }
@ -188,8 +188,8 @@ fn empty_record() {
derive_test(ToEncoder, v!(EMPTY_RECORD), |golden| { derive_test(ToEncoder, v!(EMPTY_RECORD), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for {} # derived for {}
# {} -[[toEncoder_{}(0)]]-> Encoder fmt | fmt has EncoderFormatting # {} -[[toEncoder_{}(0)]]-> Encoder fmt where fmt implements EncoderFormatting
# {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) | fmt has EncoderFormatting # {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) where fmt implements EncoderFormatting
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_{}(0)]] # @<1>: [[toEncoder_{}(0)]]
# @<2>: [[custom(2) {}]] # @<2>: [[custom(2) {}]]
@ -208,8 +208,8 @@ fn zero_field_record() {
derive_test(ToEncoder, v!({}), |golden| { derive_test(ToEncoder, v!({}), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for {} # derived for {}
# {} -[[toEncoder_{}(0)]]-> Encoder fmt | fmt has EncoderFormatting # {} -[[toEncoder_{}(0)]]-> Encoder fmt where fmt implements EncoderFormatting
# {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) | fmt has EncoderFormatting # {} -[[toEncoder_{}(0)]]-> (List U8, fmt -[[custom(2) {}]]-> List U8) where fmt implements EncoderFormatting
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_{}(0)]] # @<1>: [[toEncoder_{}(0)]]
# @<2>: [[custom(2) {}]] # @<2>: [[custom(2) {}]]
@ -228,11 +228,11 @@ fn one_field_record() {
derive_test(ToEncoder, v!({ a: v!(U8), }), |golden| { derive_test(ToEncoder, v!({ a: v!(U8), }), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for { a : U8 } # derived for { a : U8 }
# { a : val } -[[toEncoder_{a}(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding # { a : val } -[[toEncoder_{a}(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding
# { a : val } -[[toEncoder_{a}(0)]]-> (List U8, fmt -[[custom(2) { a : val }]]-> List U8) | fmt has EncoderFormatting, val has Encoding # { a : val } -[[toEncoder_{a}(0)]]-> (List U8, fmt -[[custom(2) { a : val }]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_{a}(0)]] # @<1>: [[toEncoder_{a}(0)]]
# @<2>: [[custom(2) { a : val }]] | val has Encoding # @<2>: [[custom(2) { a : val }]] where val implements Encoding
#Derived.toEncoder_{a} = #Derived.toEncoder_{a} =
\#Derived.rcd -> \#Derived.rcd ->
custom custom
@ -251,11 +251,11 @@ fn two_field_record() {
derive_test(ToEncoder, v!({ a: v!(U8), b: v!(STR), }), |golden| { derive_test(ToEncoder, v!({ a: v!(U8), b: v!(STR), }), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for { a : U8, b : Str } # derived for { a : U8, b : Str }
# { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> (List U8, fmt -[[custom(2) { a : val, b : val1 }]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # { a : val, b : val1 } -[[toEncoder_{a,b}(0)]]-> (List U8, fmt -[[custom(2) { a : val, b : val1 }]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_{a,b}(0)]] # @<1>: [[toEncoder_{a,b}(0)]]
# @<2>: [[custom(2) { a : val, b : val1 }]] | val has Encoding, val1 has Encoding # @<2>: [[custom(2) { a : val, b : val1 }]] where val implements Encoding, val1 implements Encoding
#Derived.toEncoder_{a,b} = #Derived.toEncoder_{a,b} =
\#Derived.rcd -> \#Derived.rcd ->
custom custom
@ -278,11 +278,11 @@ fn two_field_tuple() {
derive_test(ToEncoder, v!((v!(U8), v!(STR),)), |golden| { derive_test(ToEncoder, v!((v!(U8), v!(STR),)), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for ( U8, Str )* # derived for ( U8, Str )*
# ( val, val1 )* -[[toEncoder_(arity:2)(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # ( val, val1 )* -[[toEncoder_(arity:2)(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# ( val, val1 )a -[[toEncoder_(arity:2)(0)]]-> (List U8, fmt -[[custom(2) ( val, val1 )a]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # ( val, val1 )a -[[toEncoder_(arity:2)(0)]]-> (List U8, fmt -[[custom(2) ( val, val1 )a]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_(arity:2)(0)]] # @<1>: [[toEncoder_(arity:2)(0)]]
# @<2>: [[custom(2) ( val, val1 )*]] | val has Encoding, val1 has Encoding # @<2>: [[custom(2) ( val, val1 )*]] where val implements Encoding, val1 implements Encoding
#Derived.toEncoder_(arity:2) = #Derived.toEncoder_(arity:2) =
\#Derived.tup -> \#Derived.tup ->
custom custom
@ -314,8 +314,8 @@ fn tag_one_label_zero_args() {
derive_test(ToEncoder, v!([A]), |golden| { derive_test(ToEncoder, v!([A]), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [A] # derived for [A]
# [A] -[[toEncoder_[A 0](0)]]-> Encoder fmt | fmt has EncoderFormatting # [A] -[[toEncoder_[A 0](0)]]-> Encoder fmt where fmt implements EncoderFormatting
# [A] -[[toEncoder_[A 0](0)]]-> (List U8, fmt -[[custom(2) [A]]]-> List U8) | fmt has EncoderFormatting # [A] -[[toEncoder_[A 0](0)]]-> (List U8, fmt -[[custom(2) [A]]]-> List U8) where fmt implements EncoderFormatting
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_[A 0](0)]] # @<1>: [[toEncoder_[A 0](0)]]
# @<2>: [[custom(2) [A]]] # @<2>: [[custom(2) [A]]]
@ -338,11 +338,11 @@ fn tag_one_label_two_args() {
derive_test(ToEncoder, v!([A v!(U8) v!(STR)]), |golden| { derive_test(ToEncoder, v!([A v!(U8) v!(STR)]), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [A U8 Str] # derived for [A U8 Str]
# [A val val1] -[[toEncoder_[A 2](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # [A val val1] -[[toEncoder_[A 2](0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# [A val val1] -[[toEncoder_[A 2](0)]]-> (List U8, fmt -[[custom(4) [A val val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # [A val val1] -[[toEncoder_[A 2](0)]]-> (List U8, fmt -[[custom(4) [A val val1]]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_[A 2](0)]] # @<1>: [[toEncoder_[A 2](0)]]
# @<2>: [[custom(4) [A val val1]]] | val has Encoding, val1 has Encoding # @<2>: [[custom(4) [A val val1]]] where val implements Encoding, val1 implements Encoding
#Derived.toEncoder_[A 2] = #Derived.toEncoder_[A 2] =
\#Derived.tag -> \#Derived.tag ->
custom custom
@ -366,11 +366,11 @@ fn tag_two_labels() {
|golden| { |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [A U8 Str U16, B Str] # derived for [A U8 Str U16, B Str]
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # [A val val1 val1, B val1] -[[toEncoder_[A 3,B 1](0)]]-> (List U8, fmt -[[custom(6) [A val val1 val1, B val1]]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_[A 3,B 1](0)]] # @<1>: [[toEncoder_[A 3,B 1](0)]]
# @<2>: [[custom(6) [A val val1 val1, B val1]]] | val has Encoding, val1 has Encoding # @<2>: [[custom(6) [A val val1 val1, B val1]]] where val implements Encoding, val1 implements Encoding
#Derived.toEncoder_[A 3,B 1] = #Derived.toEncoder_[A 3,B 1] =
\#Derived.tag -> \#Derived.tag ->
custom custom
@ -402,11 +402,11 @@ fn recursive_tag_union() {
|golden| { |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [Cons U8 $rec, Nil] as $rec # derived for [Cons U8 $rec, Nil] as $rec
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) | fmt has EncoderFormatting, val has Encoding, val1 has Encoding # [Cons val val1, Nil] -[[toEncoder_[Cons 2,Nil 0](0)]]-> (List U8, fmt -[[custom(4) [Cons val val1, Nil]]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding, val1 implements Encoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_[Cons 2,Nil 0](0)]] # @<1>: [[toEncoder_[Cons 2,Nil 0](0)]]
# @<2>: [[custom(4) [Cons val val1, Nil]]] | val has Encoding, val1 has Encoding # @<2>: [[custom(4) [Cons val val1, Nil]]] where val implements Encoding, val1 implements Encoding
#Derived.toEncoder_[Cons 2,Nil 0] = #Derived.toEncoder_[Cons 2,Nil 0] =
\#Derived.tag -> \#Derived.tag ->
custom custom
@ -429,11 +429,11 @@ fn list() {
derive_test(ToEncoder, v!(Symbol::LIST_LIST v!(STR)), |golden| { derive_test(ToEncoder, v!(Symbol::LIST_LIST v!(STR)), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for List Str # derived for List Str
# List val -[[toEncoder_list(0)]]-> Encoder fmt | fmt has EncoderFormatting, val has Encoding # List val -[[toEncoder_list(0)]]-> Encoder fmt where fmt implements EncoderFormatting, val implements Encoding
# List val -[[toEncoder_list(0)]]-> (List U8, fmt -[[custom(4) (List val)]]-> List U8) | fmt has EncoderFormatting, val has Encoding # List val -[[toEncoder_list(0)]]-> (List U8, fmt -[[custom(4) (List val)]]-> List U8) where fmt implements EncoderFormatting, val implements Encoding
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[toEncoder_list(0)]] # @<1>: [[toEncoder_list(0)]]
# @<2>: [[custom(4) (List val)]] | val has Encoding # @<2>: [[custom(4) (List val)]] where val implements Encoding
#Derived.toEncoder_list = #Derived.toEncoder_list =
\#Derived.lst -> \#Derived.lst ->
custom custom

View file

@ -102,7 +102,7 @@ fn derivable_record_ext_flex_var() {
fn derivable_record_ext_flex_able_var() { fn derivable_record_ext_flex_able_var() {
check_derivable( check_derivable(
Hash, Hash,
v!({ a: v!(STR), }a has Symbol::DECODE_DECODER ), v!({ a: v!(STR), }a implements Symbol::DECODE_DECODER ),
DeriveKey::Hash(FlatHashKey::Record(vec!["a".into()])), DeriveKey::Hash(FlatHashKey::Record(vec!["a".into()])),
); );
} }
@ -129,7 +129,7 @@ fn derivable_tag_ext_flex_var() {
fn derivable_tag_ext_flex_able_var() { fn derivable_tag_ext_flex_able_var() {
check_derivable( check_derivable(
Hash, Hash,
v!([ A v!(STR) ]a has Symbol::ENCODE_TO_ENCODER), v!([ A v!(STR) ]a implements Symbol::ENCODE_TO_ENCODER),
DeriveKey::Hash(FlatHashKey::TagUnion(vec![("A".into(), 1)])), DeriveKey::Hash(FlatHashKey::TagUnion(vec![("A".into(), 1)])),
); );
} }
@ -151,8 +151,8 @@ fn empty_record() {
derive_test(Hash, v!(EMPTY_RECORD), |golden| { derive_test(Hash, v!(EMPTY_RECORD), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for {} # derived for {}
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher # hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher # hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_{}(0)]] # @<1>: [[hash_{}(0)]]
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher #Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
@ -166,8 +166,8 @@ fn zero_field_record() {
derive_test(Hash, v!({}), |golden| { derive_test(Hash, v!({}), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for {} # derived for {}
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher # hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
# hasher, {} -[[hash_{}(0)]]-> hasher | hasher has Hasher # hasher, {} -[[hash_{}(0)]]-> hasher where hasher implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_{}(0)]] # @<1>: [[hash_{}(0)]]
#Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher #Derived.hash_{} = \#Derived.hasher, #Derived.rcd -> #Derived.hasher
@ -181,8 +181,8 @@ fn one_field_record() {
derive_test(Hash, v!({ a: v!(U8), }), |golden| { derive_test(Hash, v!({ a: v!(U8), }), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for { a : U8 } # derived for { a : U8 }
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher | a has Hash, hasher has Hasher # hasher, { a : a } -[[hash_{a}(0)]]-> hasher where a implements Hash, hasher implements Hasher
# hasher, { a : a } -[[hash_{a}(0)]]-> hasher | a has Hash, hasher has Hasher # hasher, { a : a } -[[hash_{a}(0)]]-> hasher where a implements Hash, hasher implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_{a}(0)]] # @<1>: [[hash_{a}(0)]]
#Derived.hash_{a} = #Derived.hash_{a} =
@ -197,8 +197,8 @@ fn two_field_record() {
derive_test(Hash, v!({ a: v!(U8), b: v!(STR), }), |golden| { derive_test(Hash, v!({ a: v!(U8), b: v!(STR), }), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for { a : U8, b : Str } # derived for { a : U8, b : Str }
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher # hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
# hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher # hasher, { a : a, b : a1 } -[[hash_{a,b}(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_{a,b}(0)]] # @<1>: [[hash_{a,b}(0)]]
#Derived.hash_{a,b} = #Derived.hash_{a,b} =
@ -214,8 +214,8 @@ fn two_element_tuple() {
derive_test(Hash, v!((v!(U8), v!(STR),)), |golden| { derive_test(Hash, v!((v!(U8), v!(STR),)), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for ( U8, Str )* # derived for ( U8, Str )*
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher # hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
# hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher # hasher, ( a, a1 )* -[[hash_(arity:2)(0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_(arity:2)(0)]] # @<1>: [[hash_(arity:2)(0)]]
#Derived.hash_(arity:2) = #Derived.hash_(arity:2) =
@ -231,8 +231,8 @@ fn tag_one_label_no_payloads() {
derive_test(Hash, v!([A]), |golden| { derive_test(Hash, v!([A]), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [A] # derived for [A]
# hasher, [A] -[[hash_[A 0](0)]]-> hasher | hasher has Hasher # hasher, [A] -[[hash_[A 0](0)]]-> hasher where hasher implements Hasher
# hasher, [A] -[[hash_[A 0](0)]]-> hasher | hasher has Hasher # hasher, [A] -[[hash_[A 0](0)]]-> hasher where hasher implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_[A 0](0)]] # @<1>: [[hash_[A 0](0)]]
#Derived.hash_[A 0] = \#Derived.hasher, A -> #Derived.hasher #Derived.hash_[A 0] = \#Derived.hasher, A -> #Derived.hasher
@ -246,8 +246,8 @@ fn tag_one_label_newtype() {
derive_test(Hash, v!([A v!(U8) v!(STR)]), |golden| { derive_test(Hash, v!([A v!(U8) v!(STR)]), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [A U8 Str] # derived for [A U8 Str]
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher # hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
# hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher | a has Hash, a1 has Hash, hasher has Hasher # hasher, [A a a1] -[[hash_[A 2](0)]]-> hasher where a implements Hash, a1 implements Hash, hasher implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_[A 2](0)]] # @<1>: [[hash_[A 2](0)]]
#Derived.hash_[A 2] = #Derived.hash_[A 2] =
@ -263,8 +263,8 @@ fn tag_two_labels() {
derive_test(Hash, v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]), |golden| { derive_test(Hash, v!([A v!(U8) v!(STR) v!(U16), B v!(STR)]), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [A U8 Str U16, B Str] # derived for [A U8 Str U16, B Str]
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash, a3 has Hash # a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash, a3 implements Hash
# a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash, a3 has Hash # a, [A a1 a2 a3, B a3] -[[hash_[A 3,B 1](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash, a3 implements Hash
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_[A 3,B 1](0)]] # @<1>: [[hash_[A 3,B 1](0)]]
#Derived.hash_[A 3,B 1] = #Derived.hash_[A 3,B 1] =
@ -285,8 +285,8 @@ fn tag_two_labels_no_payloads() {
derive_test(Hash, v!([A, B]), |golden| { derive_test(Hash, v!([A, B]), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [A, B] # derived for [A, B]
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a | a has Hasher # a, [A, B] -[[hash_[A 0,B 0](0)]]-> a where a implements Hasher
# a, [A, B] -[[hash_[A 0,B 0](0)]]-> a | a has Hasher # a, [A, B] -[[hash_[A 0,B 0](0)]]-> a where a implements Hasher
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_[A 0,B 0](0)]] # @<1>: [[hash_[A 0,B 0](0)]]
#Derived.hash_[A 0,B 0] = #Derived.hash_[A 0,B 0] =
@ -304,8 +304,8 @@ fn recursive_tag_union() {
derive_test(Hash, v!([Nil, Cons v!(U8) v!(^lst) ] as lst), |golden| { derive_test(Hash, v!([Nil, Cons v!(U8) v!(^lst) ] as lst), |golden| {
assert_snapshot!(golden, @r###" assert_snapshot!(golden, @r###"
# derived for [Cons U8 $rec, Nil] as $rec # derived for [Cons U8 $rec, Nil] as $rec
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash # a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash
# a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a | a has Hasher, a1 has Hash, a2 has Hash # a, [Cons a1 a2, Nil] -[[hash_[Cons 2,Nil 0](0)]]-> a where a implements Hasher, a1 implements Hash, a2 implements Hash
# Specialization lambda sets: # Specialization lambda sets:
# @<1>: [[hash_[Cons 2,Nil 0](0)]] # @<1>: [[hash_[Cons 2,Nil 0](0)]]
#Derived.hash_[Cons 2,Nil 0] = #Derived.hash_[Cons 2,Nil 0] =

View file

@ -187,7 +187,7 @@ macro_rules! v {
use roc_types::subs::{Subs, Content}; use roc_types::subs::{Subs, Content};
|subs: &mut Subs| { roc_derive::synth_var(subs, Content::FlexVar(None)) } |subs: &mut Subs| { roc_derive::synth_var(subs, Content::FlexVar(None)) }
}}; }};
($name:ident has $ability:path) => {{ ($name:ident implements $ability:path) => {{
use roc_types::subs::{Subs, SubsIndex, SubsSlice, Content}; use roc_types::subs::{Subs, SubsIndex, SubsSlice, Content};
|subs: &mut Subs| { |subs: &mut Subs| {
let name_index = let name_index =

View file

@ -62,8 +62,13 @@ fn roc_function<'a, 'b>(
}; };
let context = inkwell::context::Context::create(); let context = inkwell::context::Context::create();
let (main_fn_name, errors, lib) = let (main_fn_name, errors, lib) = helpers::llvm::helper(
helpers::llvm::helper(arena, config, source, arena.alloc(context)); arena,
config,
source,
arena.alloc(context),
roc_load::FunctionKind::LambdaSet,
);
assert!(errors.is_empty(), "Encountered errors:\n{errors}"); assert!(errors.is_empty(), "Encountered errors:\n{errors}");

View file

@ -91,8 +91,13 @@ fn roc_function<'a>(
}; };
let context = inkwell::context::Context::create(); let context = inkwell::context::Context::create();
let (main_fn_name, errors, lib) = let (main_fn_name, errors, lib) = helpers::llvm::helper(
helpers::llvm::helper(arena, config, source, arena.alloc(context)); arena,
config,
source,
arena.alloc(context),
roc_load::FunctionKind::LambdaSet,
);
assert!(errors.is_empty(), "Encountered errors:\n{errors}"); assert!(errors.is_empty(), "Encountered errors:\n{errors}");

View file

@ -22,10 +22,10 @@ fn hash_specialization() {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
Id := U64 has [MHash {hash}] Id := U64 implements [MHash {hash}]
hash = \@Id n -> n hash = \@Id n -> n
@ -45,14 +45,14 @@ fn hash_specialization_multiple_add() {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
Id := U64 has [ MHash {hash: hashId} ] Id := U64 implements [ MHash {hash: hashId} ]
hashId = \@Id n -> n hashId = \@Id n -> n
One := {} has [ MHash {hash: hashOne} ] One := {} implements [ MHash {hash: hashOne} ]
hashOne = \@One _ -> 1 hashOne = \@One _ -> 1
@ -72,10 +72,10 @@ fn alias_member_specialization() {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
Id := U64 has [MHash {hash}] Id := U64 implements [MHash {hash}]
hash = \@Id n -> n hash = \@Id n -> n
@ -97,13 +97,13 @@ fn ability_constrained_in_non_member_usage() {
r#" r#"
app "test" provides [result] to "./platform" app "test" provides [result] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
mulMHashes : a, a -> U64 | a has MHash mulMHashes : a, a -> U64 where a implements MHash
mulMHashes = \x, y -> hash x * hash y mulMHashes = \x, y -> hash x * hash y
Id := U64 has [MHash {hash}] Id := U64 implements [MHash {hash}]
hash = \@Id n -> n hash = \@Id n -> n
result = mulMHashes (@Id 5) (@Id 7) result = mulMHashes (@Id 5) (@Id 7)
@ -122,12 +122,12 @@ fn ability_constrained_in_non_member_usage_inferred() {
r#" r#"
app "test" provides [result] to "./platform" app "test" provides [result] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
mulMHashes = \x, y -> hash x * hash y mulMHashes = \x, y -> hash x * hash y
Id := U64 has [MHash {hash}] Id := U64 implements [MHash {hash}]
hash = \@Id n -> n hash = \@Id n -> n
result = mulMHashes (@Id 5) (@Id 7) result = mulMHashes (@Id 5) (@Id 7)
@ -146,16 +146,16 @@ fn ability_constrained_in_non_member_multiple_specializations() {
r#" r#"
app "test" provides [result] to "./platform" app "test" provides [result] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
mulMHashes : a, b -> U64 | a has MHash, b has MHash mulMHashes : a, b -> U64 where a implements MHash, b implements MHash
mulMHashes = \x, y -> hash x * hash y mulMHashes = \x, y -> hash x * hash y
Id := U64 has [MHash { hash: hashId }] Id := U64 implements [MHash { hash: hashId }]
hashId = \@Id n -> n hashId = \@Id n -> n
Three := {} has [MHash { hash: hashThree }] Three := {} implements [MHash { hash: hashThree }]
hashThree = \@Three _ -> 3 hashThree = \@Three _ -> 3
result = mulMHashes (@Id 100) (@Three {}) result = mulMHashes (@Id 100) (@Three {})
@ -174,15 +174,15 @@ fn ability_constrained_in_non_member_multiple_specializations_inferred() {
r#" r#"
app "test" provides [result] to "./platform" app "test" provides [result] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
mulMHashes = \x, y -> hash x * hash y mulMHashes = \x, y -> hash x * hash y
Id := U64 has [MHash { hash: hashId }] Id := U64 implements [MHash { hash: hashId }]
hashId = \@Id n -> n hashId = \@Id n -> n
Three := {} has [MHash { hash: hashThree }] Three := {} implements [MHash { hash: hashThree }]
hashThree = \@Three _ -> 3 hashThree = \@Three _ -> 3
result = mulMHashes (@Id 100) (@Three {}) result = mulMHashes (@Id 100) (@Three {})
@ -201,16 +201,16 @@ fn ability_used_as_type_still_compiles() {
r#" r#"
app "test" provides [result] to "./platform" app "test" provides [result] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
mulMHashes : MHash, MHash -> U64 mulMHashes : MHash, MHash -> U64
mulMHashes = \x, y -> hash x * hash y mulMHashes = \x, y -> hash x * hash y
Id := U64 has [MHash { hash: hashId }] Id := U64 implements [MHash { hash: hashId }]
hashId = \@Id n -> n hashId = \@Id n -> n
Three := {} has [MHash { hash: hashThree }] Three := {} implements [MHash { hash: hashThree }]
hashThree = \@Three _ -> 3 hashThree = \@Three _ -> 3
result = mulMHashes (@Id 100) (@Three {}) result = mulMHashes (@Id 100) (@Three {})
@ -229,15 +229,15 @@ fn bounds_to_multiple_abilities() {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
Idempot has idempot : a -> a | a has Idempot Idempot implements idempot : a -> a where a implements Idempot
Consume has consume : a -> Str | a has Consume Consume implements consume : a -> Str where a implements Consume
Hello := Str has [Idempot { idempot: idempotHello }, Consume { consume: consumeHello }] Hello := Str implements [Idempot { idempot: idempotHello }, Consume { consume: consumeHello }]
idempotHello = \@Hello msg -> @Hello msg idempotHello = \@Hello msg -> @Hello msg
consumeHello = \@Hello msg -> msg consumeHello = \@Hello msg -> msg
lifecycle : a -> Str | a has Idempot & Consume lifecycle : a -> Str where a implements Idempot & Consume
lifecycle = \x -> idempot x |> consume lifecycle = \x -> idempot x |> consume
main = lifecycle (@Hello "hello world") main = lifecycle (@Hello "hello world")
@ -256,26 +256,26 @@ fn encode() {
r#" r#"
app "test" provides [myU8Bytes] to "./platform" app "test" provides [myU8Bytes] to "./platform"
MEncoder fmt := List U8, fmt -> List U8 | fmt has Format MEncoder fmt := List U8, fmt -> List U8 where fmt implements Format
MEncoding has MEncoding implements
toEncoder : val -> MEncoder fmt | val has MEncoding, fmt has Format toEncoder : val -> MEncoder fmt where val implements MEncoding, fmt implements Format
Format has Format implements
u8 : U8 -> MEncoder fmt | fmt has Format u8 : U8 -> MEncoder fmt where fmt implements Format
appendWith : List U8, MEncoder fmt, fmt -> List U8 | fmt has Format appendWith : List U8, MEncoder fmt, fmt -> List U8 where fmt implements Format
appendWith = \lst, (@MEncoder doFormat), fmt -> doFormat lst fmt appendWith = \lst, (@MEncoder doFormat), fmt -> doFormat lst fmt
toBytes : val, fmt -> List U8 | val has MEncoding, fmt has Format toBytes : val, fmt -> List U8 where val implements MEncoding, fmt implements Format
toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt toBytes = \val, fmt -> appendWith [] (toEncoder val) fmt
Linear := {} has [Format {u8}] Linear := {} implements [Format {u8}]
u8 = \n -> @MEncoder (\lst, @Linear {} -> List.append lst n) u8 = \n -> @MEncoder (\lst, @Linear {} -> List.append lst n)
Rgba := { r : U8, g : U8, b : U8, a : U8 } has [MEncoding {toEncoder}] Rgba := { r : U8, g : U8, b : U8, a : U8 } implements [MEncoding {toEncoder}]
toEncoder = \@Rgba {r, g, b, a} -> toEncoder = \@Rgba {r, g, b, a} ->
@MEncoder \lst, fmt -> lst @MEncoder \lst, fmt -> lst
@ -303,19 +303,19 @@ fn decode() {
MDecodeError : [TooShort, Leftover (List U8)] MDecodeError : [TooShort, Leftover (List U8)]
MDecoder val fmt := List U8, fmt -> { result: Result val MDecodeError, rest: List U8 } | fmt has MDecoderFormatting MDecoder val fmt := List U8, fmt -> { result: Result val MDecodeError, rest: List U8 } where fmt implements MDecoderFormatting
MDecoding has MDecoding implements
decoder : MDecoder val fmt | val has MDecoding, fmt has MDecoderFormatting decoder : MDecoder val fmt where val implements MDecoding, fmt implements MDecoderFormatting
MDecoderFormatting has MDecoderFormatting implements
u8 : MDecoder U8 fmt | fmt has MDecoderFormatting u8 : MDecoder U8 fmt where fmt implements MDecoderFormatting
decodeWith : List U8, MDecoder val fmt, fmt -> { result: Result val MDecodeError, rest: List U8 } | fmt has MDecoderFormatting decodeWith : List U8, MDecoder val fmt, fmt -> { result: Result val MDecodeError, rest: List U8 } where fmt implements MDecoderFormatting
decodeWith = \lst, (@MDecoder doDecode), fmt -> doDecode lst fmt decodeWith = \lst, (@MDecoder doDecode), fmt -> doDecode lst fmt
fromBytes : List U8, fmt -> Result val MDecodeError fromBytes : List U8, fmt -> Result val MDecodeError
| fmt has MDecoderFormatting, val has MDecoding where fmt implements MDecoderFormatting, val implements MDecoding
fromBytes = \lst, fmt -> fromBytes = \lst, fmt ->
when decodeWith lst decoder fmt is when decodeWith lst decoder fmt is
{ result, rest } -> { result, rest } ->
@ -325,14 +325,14 @@ fn decode() {
else Err (Leftover rest) else Err (Leftover rest)
Linear := {} has [MDecoderFormatting {u8}] Linear := {} implements [MDecoderFormatting {u8}]
u8 = @MDecoder \lst, @Linear {} -> u8 = @MDecoder \lst, @Linear {} ->
when List.first lst is when List.first lst is
Ok n -> { result: Ok n, rest: List.dropFirst lst } Ok n -> { result: Ok n, rest: List.dropFirst lst }
Err _ -> { result: Err TooShort, rest: [] } Err _ -> { result: Err TooShort, rest: [] }
MyU8 := U8 has [MDecoding {decoder}] MyU8 := U8 implements [MDecoding {decoder}]
# impl MDecoding for MyU8 # impl MDecoding for MyU8
decoder = @MDecoder \lst, fmt -> decoder = @MDecoder \lst, fmt ->
@ -360,7 +360,7 @@ fn encode_use_stdlib() {
imports [Encode, TotallyNotJson] imports [Encode, TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
HelloWorld := {} has [Encoding {toEncoder}] HelloWorld := {} implements [Encoding {toEncoder}]
toEncoder = \@HelloWorld {} -> toEncoder = \@HelloWorld {} ->
Encode.custom \bytes, fmt -> Encode.custom \bytes, fmt ->
bytes bytes
@ -388,7 +388,7 @@ fn encode_use_stdlib_without_wrapping_custom() {
imports [Encode, TotallyNotJson] imports [Encode, TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
HelloWorld := {} has [Encoding {toEncoder}] HelloWorld := {} implements [Encoding {toEncoder}]
toEncoder = \@HelloWorld {} -> Encode.string "Hello, World!\n" toEncoder = \@HelloWorld {} -> Encode.string "Hello, World!\n"
main = main =
@ -414,7 +414,7 @@ fn encode_derive_to_encoder_for_opaque() {
imports [TotallyNotJson] imports [TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
HelloWorld := { a: Str } has [Encoding] HelloWorld := { a: Str } implements [Encoding]
main = main =
result = Str.fromUtf8 (Encode.toBytes (@HelloWorld { a: "Hello, World!" }) TotallyNotJson.json) result = Str.fromUtf8 (Encode.toBytes (@HelloWorld { a: "Hello, World!" }) TotallyNotJson.json)
@ -438,7 +438,7 @@ fn to_encoder_encode_custom_has_capture() {
imports [Encode, TotallyNotJson] imports [Encode, TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
HelloWorld := Str has [Encoding {toEncoder}] HelloWorld := Str implements [Encoding {toEncoder}]
toEncoder = \@HelloWorld s1 -> toEncoder = \@HelloWorld s1 ->
Encode.custom \bytes, fmt -> Encode.custom \bytes, fmt ->
bytes bytes
@ -891,7 +891,7 @@ fn encode_derived_generic_record_with_different_field_types() {
imports [Encode, TotallyNotJson] imports [Encode, TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
Q a b := {a: a, b: b} has [Encoding] Q a b := {a: a, b: b} implements [Encoding]
q = @Q {a: 10u32, b: "fieldb"} q = @Q {a: 10u32, b: "fieldb"}
@ -917,7 +917,7 @@ fn encode_derived_generic_tag_with_different_field_types() {
imports [Encode, TotallyNotJson] imports [Encode, TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
Q a b := [A a, B b] has [Encoding] Q a b := [A a, B b] implements [Encoding]
q : Q Str U32 q : Q Str U32
q = @Q (B 67) q = @Q (B 67)
@ -969,7 +969,7 @@ fn decode_use_stdlib() {
imports [TotallyNotJson] imports [TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
MyNum := U8 has [Decoding {decoder: myDecoder}] MyNum := U8 implements [Decoding {decoder: myDecoder}]
myDecoder = myDecoder =
Decode.custom \bytes, fmt -> Decode.custom \bytes, fmt ->
@ -1003,7 +1003,7 @@ fn decode_derive_decoder_for_opaque() {
imports [TotallyNotJson] imports [TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
HelloWorld := { a: Str } has [Decoding] HelloWorld := { a: Str } implements [Decoding]
main = main =
when Str.toUtf8 """{"a":"Hello, World!"}""" |> Decode.fromBytes TotallyNotJson.json is when Str.toUtf8 """{"a":"Hello, World!"}""" |> Decode.fromBytes TotallyNotJson.json is
@ -1026,7 +1026,7 @@ fn decode_use_stdlib_json_list() {
imports [TotallyNotJson] imports [TotallyNotJson]
provides [main] to "./platform" provides [main] to "./platform"
MyNumList := List U8 has [Decoding {decoder: myDecoder}] MyNumList := List U8 implements [Decoding {decoder: myDecoder}]
myDecoder = myDecoder =
Decode.custom \bytes, fmt -> Decode.custom \bytes, fmt ->
@ -1459,7 +1459,7 @@ mod hash {
const TEST_HASHER: &str = indoc!( const TEST_HASHER: &str = indoc!(
r#" r#"
THasher := List U8 has [Hasher { THasher := List U8 implements [Hasher {
addBytes: tAddBytes, addBytes: tAddBytes,
addU8: tAddU8, addU8: tAddU8,
addU16: tAddU16, addU16: tAddU16,
@ -2002,7 +2002,7 @@ mod hash {
{} {}
Q := {{ a: U8, b: U8, c: U8 }} has [Hash] Q := {{ a: U8, b: U8, c: U8 }} implements [Hash]
q = @Q {{ a: 15, b: 27, c: 31 }} q = @Q {{ a: 15, b: 27, c: 31 }}
@ -2055,7 +2055,7 @@ mod eq {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
LyingEq := U8 has [Eq {isEq}] LyingEq := U8 implements [Eq {isEq}]
isEq = \@LyingEq m, @LyingEq n -> m != n isEq = \@LyingEq m, @LyingEq n -> m != n
@ -2081,7 +2081,7 @@ mod eq {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
Q := ({} -> Str) has [Eq {isEq: isEqQ}] Q := ({} -> Str) implements [Eq {isEq: isEqQ}]
isEqQ = \@Q _, @Q _ -> Bool.true isEqQ = \@Q _, @Q _ -> Bool.true
@ -2101,7 +2101,7 @@ mod eq {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
Q := ({} -> Str) has [Eq {isEq: isEqQ}] Q := ({} -> Str) implements [Eq {isEq: isEqQ}]
isEqQ = \@Q f1, @Q f2 -> (f1 {} == f2 {}) isEqQ = \@Q f1, @Q f2 -> (f1 {} == f2 {})
@ -2135,7 +2135,7 @@ mod eq {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
Q := U8 has [Eq] Q := U8 implements [Eq]
main = (@Q 15) == (@Q 15) main = (@Q 15) == (@Q 15)
"# "#

View file

@ -959,7 +959,7 @@ fn list_walk_implements_position() {
r#" r#"
Option a : [Some a, None] Option a : [Some a, None]
find : List a, a -> Option Nat | a has Eq find : List a, a -> Option Nat where a implements Eq
find = \list, needle -> find = \list, needle ->
findHelp list needle findHelp list needle
|> .v |> .v
@ -3692,7 +3692,7 @@ fn list_walk_backwards_implements_position() {
r#" r#"
Option a : [Some a, None] Option a : [Some a, None]
find : List a, a -> Option Nat | a has Eq find : List a, a -> Option Nat where a implements Eq
find = \list, needle -> find = \list, needle ->
findHelp list needle findHelp list needle
|> .v |> .v
@ -3810,6 +3810,9 @@ fn list_range_length_overflow() {
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
mod pattern_match { mod pattern_match {
#[allow(unused_imports)]
use crate::helpers::with_larger_debug_stack;
#[cfg(feature = "gen-llvm")] #[cfg(feature = "gen-llvm")]
use crate::helpers::llvm::assert_evals_to; use crate::helpers::llvm::assert_evals_to;
@ -3819,8 +3822,6 @@ mod pattern_match {
#[cfg(feature = "gen-dev")] #[cfg(feature = "gen-dev")]
use crate::helpers::dev::assert_evals_to; use crate::helpers::dev::assert_evals_to;
use crate::helpers::with_larger_debug_stack;
use super::RocList; use super::RocList;
#[test] #[test]

View file

@ -891,7 +891,7 @@ fn gen_wrap_int_neq() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
wrappedNotEq : a, a -> Bool | a has Eq wrappedNotEq : a, a -> Bool where a implements Eq
wrappedNotEq = \num1, num2 -> wrappedNotEq = \num1, num2 ->
num1 != num2 num1 != num2

View file

@ -1,3 +1,6 @@
#[allow(unused_imports)]
use crate::helpers::with_larger_debug_stack;
#[cfg(feature = "gen-llvm")] #[cfg(feature = "gen-llvm")]
use crate::helpers::llvm::assert_evals_to; use crate::helpers::llvm::assert_evals_to;
@ -14,8 +17,6 @@ use roc_mono::layout::{LayoutRepr, STLayoutInterner};
#[cfg(test)] #[cfg(test)]
use roc_std::{RocList, RocStr, U128}; use roc_std::{RocList, RocStr, U128};
use crate::helpers::with_larger_debug_stack;
#[test] #[test]
fn width_and_alignment_u8_u8() { fn width_and_alignment_u8_u8() {
use roc_mono::layout::Layout; use roc_mono::layout::Layout;

View file

@ -1349,10 +1349,10 @@ fn specialize_ability_call() {
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
MHash has MHash implements
hash : a -> U64 | a has MHash hash : a -> U64 where a implements MHash
Id := U64 has [MHash {hash}] Id := U64 implements [MHash {hash}]
hash : Id -> U64 hash : Id -> U64
hash = \@Id n -> n hash = \@Id n -> n
@ -1385,20 +1385,20 @@ fn encode() {
r#" r#"
app "test" provides [myU8Bytes] to "./platform" app "test" provides [myU8Bytes] to "./platform"
MEncoder fmt := List U8, fmt -> List U8 | fmt has Format MEncoder fmt := List U8, fmt -> List U8 where fmt implements Format
MEncoding has MEncoding implements
toEncoder : val -> MEncoder fmt | val has MEncoding, fmt has Format toEncoder : val -> MEncoder fmt where val implements MEncoding, fmt implements Format
Format has Format implements
u8 : U8 -> MEncoder fmt | fmt has Format u8 : U8 -> MEncoder fmt where fmt implements Format
Linear := {} has [Format {u8}] Linear := {} implements [Format {u8}]
u8 = \n -> @MEncoder (\lst, @Linear {} -> List.append lst n) u8 = \n -> @MEncoder (\lst, @Linear {} -> List.append lst n)
MyU8 := U8 has [MEncoding {toEncoder}] MyU8 := U8 implements [MEncoding {toEncoder}]
toEncoder = \@MyU8 n -> u8 n toEncoder = \@MyU8 n -> u8 n
@ -2579,20 +2579,20 @@ fn unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unifica
r#" r#"
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
MEncoder fmt := List U8, fmt -> List U8 | fmt has Format MEncoder fmt := List U8, fmt -> List U8 where fmt implements Format
MEncoding has MEncoding implements
toEncoder : val -> MEncoder fmt | val has MEncoding, fmt has Format toEncoder : val -> MEncoder fmt where val implements MEncoding, fmt implements Format
Format has Format implements
u8 : {} -> MEncoder fmt | fmt has Format u8 : {} -> MEncoder fmt where fmt implements Format
str : {} -> MEncoder fmt | fmt has Format str : {} -> MEncoder fmt where fmt implements Format
tag : MEncoder fmt -> MEncoder fmt | fmt has Format tag : MEncoder fmt -> MEncoder fmt where fmt implements Format
Linear := {} has [Format {u8: lU8, str: lStr, tag: lTag}] Linear := {} implements [Format {u8: lU8, str: lStr, tag: lTag}]
MU8 := U8 has [MEncoding {toEncoder: toEncoderU8}] MU8 := U8 implements [MEncoding {toEncoder: toEncoderU8}]
MStr := Str has [MEncoding {toEncoder: toEncoderStr}] MStr := Str implements [MEncoding {toEncoder: toEncoderStr}]
Q a b := { a: a, b: b } Q a b := { a: a, b: b }
@ -2642,7 +2642,7 @@ fn unspecialized_lambda_set_unification_keeps_all_concrete_types_without_unifica
r#" r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform" app "test" imports [TotallyNotJson] provides [main] to "./platform"
Q a b := { a: a, b: b } has [Encoding {toEncoder: toEncoderQ}] Q a b := { a: a, b: b } implements [Encoding {toEncoder: toEncoderQ}]
toEncoderQ = toEncoderQ =
\@Q t -> Encode.custom \bytes, fmt -> \@Q t -> Encode.custom \bytes, fmt ->
@ -2680,7 +2680,7 @@ fn unspecialized_lambda_set_unification_does_not_duplicate_identical_concrete_ty
r#" r#"
app "test" imports [TotallyNotJson] provides [main] to "./platform" app "test" imports [TotallyNotJson] provides [main] to "./platform"
Q a b := { a: a, b: b } has [Encoding {toEncoder: toEncoderQ}] Q a b := { a: a, b: b } implements [Encoding {toEncoder: toEncoderQ}]
toEncoderQ = toEncoderQ =
\@Q t -> Encode.custom \bytes, fmt -> \@Q t -> Encode.custom \bytes, fmt ->

View file

@ -6,7 +6,7 @@
"is" "is"
"expect" "expect"
"dbg" "dbg"
"has" "implements"
"app" "app"
"platform" "platform"

View file

@ -1 +1 @@
Expr(Ability(DemandColon(@15), @7), @0) Expr(Ability(DemandColon(@22), @14), @0)

View file

@ -1,4 +1,4 @@
MEq has MEq implements
eq b c : a, a -> U64 | a has MEq eq b c : a, a -> U64 where a implements MEq
1 1

View file

@ -1 +1 @@
Expr(Ability(DemandAlignment(4, @49), @40), @0) Expr(Ability(DemandAlignment(4, @67), @58), @0)

View file

@ -1,5 +1,5 @@
MEq has MEq implements
eq : a, a -> U64 | a has MEq eq : a, a -> U64 where a implements MEq
neq : a, a -> U64 | a has MEq neq : a, a -> U64 where a implements MEq
1 1

View file

@ -1 +1 @@
Expr(Ability(DemandAlignment(-1, @8), @7), @0) Expr(Ability(DemandAlignment(-1, @15), @14), @0)

View file

@ -1,4 +1,4 @@
MEq has MEq implements
eq : a, a -> U64 | a has MEq eq : a, a -> U64 where a implements MEq
1 1

View file

@ -1 +1 @@
Expr(Ability(DemandName(@12), @7), @0) Expr(Ability(DemandName(@19), @14), @0)

View file

@ -1,4 +1,4 @@
MEq has MEq implements
123 123
1 1

View file

@ -1,4 +1,4 @@
Hash has Hash implements
hash : a hash : a
-> U64 -> U64

View file

@ -4,7 +4,7 @@ Defs(
Index(0), Index(0),
], ],
regions: [ regions: [
@0-36, @0-43,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,18 +19,18 @@ Defs(
name: @0-4 "Hash", name: @0-4 "Hash",
vars: [], vars: [],
}, },
loc_has: @5-8 Has, loc_implements: @5-15 Implements,
members: [ members: [
AbilityMember { AbilityMember {
name: @11-15 SpaceBefore( name: @18-22 SpaceBefore(
"hash", "hash",
[ [
Newline, Newline,
], ],
), ),
typ: @18-36 Function( typ: @25-43 Function(
[ [
@18-19 SpaceAfter( @25-26 SpaceAfter(
BoundVariable( BoundVariable(
"a", "a",
), ),
@ -39,7 +39,7 @@ Defs(
], ],
), ),
], ],
@33-36 Apply( @40-43 Apply(
"", "",
"U64", "U64",
[], [],
@ -51,7 +51,7 @@ Defs(
], ],
value_defs: [], value_defs: [],
}, },
@38-39 SpaceBefore( @45-46 SpaceBefore(
Num( Num(
"1", "1",
), ),

View file

@ -1,4 +1,4 @@
Hash has Hash implements
hash : a hash : a
-> U64 -> U64

View file

@ -1,4 +1,4 @@
Hash has Hash implements
hash : a -> U64 hash : a -> U64
hash2 : a -> U64 hash2 : a -> U64

View file

@ -4,7 +4,7 @@ Defs(
Index(0), Index(0),
], ],
regions: [ regions: [
@0-45, @0-52,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,22 +19,22 @@ Defs(
name: @0-4 "Hash", name: @0-4 "Hash",
vars: [], vars: [],
}, },
loc_has: @5-8 Has, loc_implements: @5-15 Implements,
members: [ members: [
AbilityMember { AbilityMember {
name: @11-15 SpaceBefore( name: @18-22 SpaceBefore(
"hash", "hash",
[ [
Newline, Newline,
], ],
), ),
typ: @18-26 Function( typ: @25-33 Function(
[ [
@18-19 BoundVariable( @25-26 BoundVariable(
"a", "a",
), ),
], ],
@23-26 Apply( @30-33 Apply(
"", "",
"U64", "U64",
[], [],
@ -42,19 +42,19 @@ Defs(
), ),
}, },
AbilityMember { AbilityMember {
name: @29-34 SpaceBefore( name: @36-41 SpaceBefore(
"hash2", "hash2",
[ [
Newline, Newline,
], ],
), ),
typ: @37-45 Function( typ: @44-52 Function(
[ [
@37-38 BoundVariable( @44-45 BoundVariable(
"a", "a",
), ),
], ],
@42-45 Apply( @49-52 Apply(
"", "",
"U64", "U64",
[], [],
@ -66,7 +66,7 @@ Defs(
], ],
value_defs: [], value_defs: [],
}, },
@47-48 SpaceBefore( @54-55 SpaceBefore(
Num( Num(
"1", "1",
), ),

View file

@ -1,4 +1,4 @@
Hash has Hash implements
hash : a -> U64 hash : a -> U64
hash2 : a -> U64 hash2 : a -> U64

View file

@ -4,7 +4,7 @@ Defs(
Index(0), Index(0),
], ],
regions: [ regions: [
@0-37, @0-55,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,28 +19,28 @@ Defs(
name: @0-4 "Hash", name: @0-4 "Hash",
vars: [], vars: [],
}, },
loc_has: @5-8 Has, loc_implements: @5-15 Implements,
members: [ members: [
AbilityMember { AbilityMember {
name: @9-13 "hash", name: @16-20 "hash",
typ: @16-37 Where( typ: @23-55 Where(
@16-24 Function( @23-31 Function(
[ [
@16-17 BoundVariable( @23-24 BoundVariable(
"a", "a",
), ),
], ],
@21-24 Apply( @28-31 Apply(
"", "",
"U64", "U64",
[], [],
), ),
), ),
[ [
@27-37 HasClause { @38-55 ImplementsClause {
var: @27-28 "a", var: @38-39 "a",
abilities: [ abilities: [
@33-37 Apply( @51-55 Apply(
"", "",
"Hash", "Hash",
[], [],
@ -55,7 +55,7 @@ Defs(
], ],
value_defs: [], value_defs: [],
}, },
@39-40 SpaceBefore( @57-58 SpaceBefore(
Num( Num(
"1", "1",
), ),

View file

@ -1,3 +1,3 @@
Hash has hash : a -> U64 | a has Hash Hash implements hash : a -> U64 where a implements Hash
1 1

View file

@ -5,8 +5,8 @@ Defs(
Index(1), Index(1),
], ],
regions: [ regions: [
@0-33, @0-51,
@35-68, @53-104,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -26,27 +26,27 @@ Defs(
name: @0-3 "Ab1", name: @0-3 "Ab1",
vars: [], vars: [],
}, },
loc_has: @4-7 Has, loc_implements: @4-14 Implements,
members: [ members: [
AbilityMember { AbilityMember {
name: @8-11 "ab1", name: @15-18 "ab1",
typ: @14-33 Where( typ: @21-51 Where(
@14-21 Function( @21-28 Function(
[ [
@14-15 BoundVariable( @21-22 BoundVariable(
"a", "a",
), ),
], ],
@19-21 Record { @26-28 Record {
fields: [], fields: [],
ext: None, ext: None,
}, },
), ),
[ [
@24-33 HasClause { @35-51 ImplementsClause {
var: @24-25 "a", var: @35-36 "a",
abilities: [ abilities: [
@30-33 Apply( @48-51 Apply(
"", "",
"Ab1", "Ab1",
[], [],
@ -60,30 +60,30 @@ Defs(
}, },
Ability { Ability {
header: TypeHeader { header: TypeHeader {
name: @35-38 "Ab2", name: @53-56 "Ab2",
vars: [], vars: [],
}, },
loc_has: @39-42 Has, loc_implements: @57-67 Implements,
members: [ members: [
AbilityMember { AbilityMember {
name: @43-46 "ab2", name: @68-71 "ab2",
typ: @49-68 Where( typ: @74-104 Where(
@49-56 Function( @74-81 Function(
[ [
@49-50 BoundVariable( @74-75 BoundVariable(
"a", "a",
), ),
], ],
@54-56 Record { @79-81 Record {
fields: [], fields: [],
ext: None, ext: None,
}, },
), ),
[ [
@59-68 HasClause { @88-104 ImplementsClause {
var: @59-60 "a", var: @88-89 "a",
abilities: [ abilities: [
@65-68 Apply( @101-104 Apply(
"", "",
"Ab2", "Ab2",
[], [],
@ -98,7 +98,7 @@ Defs(
], ],
value_defs: [], value_defs: [],
}, },
@70-71 SpaceBefore( @106-107 SpaceBefore(
Num( Num(
"1", "1",
), ),

View file

@ -1,5 +1,5 @@
Ab1 has ab1 : a -> {} | a has Ab1 Ab1 implements ab1 : a -> {} where a implements Ab1
Ab2 has ab2 : a -> {} | a has Ab2 Ab2 implements ab2 : a -> {} where a implements Ab2
1 1

View file

@ -1,7 +1,7 @@
app "hello" app "hello"
packages { packages {
pf: pf:
"https://github.com/roc-lang/basic-cli/releases/download/0.4.0/DI4lqn7LIZs8ZrCDUgLK-tHHpQmxGF1ZrlevRKq5LXk.tar.br", "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br",
} }
imports [pf.Stdout] imports [pf.Stdout]
provides [main] to pf provides [main] to pf

View file

@ -24,7 +24,7 @@ Full {
Newline, Newline,
], ],
package_name: @31-145 PackageName( package_name: @31-145 PackageName(
"https://github.com/roc-lang/basic-cli/releases/download/0.4.0/DI4lqn7LIZs8ZrCDUgLK-tHHpQmxGF1ZrlevRKq5LXk.tar.br", "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br",
), ),
}, },
[ [

View file

@ -1,6 +1,6 @@
app "hello" app "hello"
packages { pf: packages { pf:
"https://github.com/roc-lang/basic-cli/releases/download/0.4.0/DI4lqn7LIZs8ZrCDUgLK-tHHpQmxGF1ZrlevRKq5LXk.tar.br" "https://github.com/roc-lang/basic-cli/releases/download/0.5.0/Cufzl36_SnJ4QbOoEmiJ5dIpUxBvdB3NEySvuH82Wio.tar.br"
} }
imports [pf.Stdout] imports [pf.Stdout]
provides [main] to pf provides [main] to pf

View file

@ -1,24 +1,24 @@
A := U8 has [Eq, Hash] A := U8 implements [Eq, Hash]
A := a | a has Other A := a where a implements Other
has [Eq, Hash] implements [Eq, Hash]
A := a | a has Other A := a where a implements Other
has [Eq, Hash] implements [Eq, Hash]
A := U8 has [Eq { eq }, Hash { hash }] A := U8 implements [Eq { eq }, Hash { hash }]
A := U8 has [Eq { eq, eq1 }] A := U8 implements [Eq { eq, eq1 }]
A := U8 has [Eq { eq, eq1 }, Hash] A := U8 implements [Eq { eq, eq1 }, Hash]
A := U8 has [Hash, Eq { eq, eq1 }] A := U8 implements [Hash, Eq { eq, eq1 }]
A := U8 has [] A := U8 implements []
A := a | a has Other A := a where a implements Other
has [Eq { eq }, Hash { hash }] implements [Eq { eq }, Hash { hash }]
A := U8 has [Eq {}] A := U8 implements [Eq {}]
0 0

View file

@ -14,15 +14,15 @@ Defs(
], ],
regions: [ regions: [
@0-7, @0-7,
@24-44, @31-62,
@61-81, @86-117,
@103-110, @146-153,
@139-146, @189-196,
@167-174, @224-231,
@201-208, @265-272,
@235-242, @306-313,
@251-271, @329-360,
@305-312, @401-408,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -80,18 +80,18 @@ Defs(
[], [],
), ),
derived: Some( derived: Some(
@12-22 Has( @19-29 Implements(
[ [
@13-15 HasAbility { @20-22 ImplementsAbility {
ability: @13-15 Apply( ability: @20-22 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: None, impls: None,
}, },
@17-21 HasAbility { @24-28 ImplementsAbility {
ability: @17-21 Apply( ability: @24-28 Apply(
"", "",
"Hash", "Hash",
[], [],
@ -104,18 +104,18 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @24-25 "A", name: @31-32 "A",
vars: [], vars: [],
}, },
typ: @29-44 Where( typ: @36-62 Where(
@29-30 BoundVariable( @36-37 BoundVariable(
"a", "a",
), ),
[ [
@33-44 HasClause { @44-62 ImplementsClause {
var: @33-34 "a", var: @44-45 "a",
abilities: [ abilities: [
@39-44 Apply( @57-62 Apply(
"", "",
"Other", "Other",
[], [],
@ -125,18 +125,18 @@ Defs(
], ],
), ),
derived: Some( derived: Some(
@49-59 Has( @74-84 Implements(
[ [
@50-52 HasAbility { @75-77 ImplementsAbility {
ability: @50-52 Apply( ability: @75-77 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: None, impls: None,
}, },
@54-58 HasAbility { @79-83 ImplementsAbility {
ability: @54-58 Apply( ability: @79-83 Apply(
"", "",
"Hash", "Hash",
[], [],
@ -149,18 +149,18 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @61-62 "A", name: @86-87 "A",
vars: [], vars: [],
}, },
typ: @66-81 Where( typ: @91-117 Where(
@66-67 BoundVariable( @91-92 BoundVariable(
"a", "a",
), ),
[ [
@70-81 HasClause { @99-117 ImplementsClause {
var: @70-71 "a", var: @99-100 "a",
abilities: [ abilities: [
@76-81 Apply( @112-117 Apply(
"", "",
"Other", "Other",
[], [],
@ -170,19 +170,19 @@ Defs(
], ],
), ),
derived: Some( derived: Some(
@91-101 SpaceBefore( @134-144 SpaceBefore(
Has( Implements(
[ [
@92-94 HasAbility { @135-137 ImplementsAbility {
ability: @92-94 Apply( ability: @135-137 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: None, impls: None,
}, },
@96-100 HasAbility { @139-143 ImplementsAbility {
ability: @96-100 Apply( ability: @139-143 Apply(
"", "",
"Hash", "Hash",
[], [],
@ -199,44 +199,44 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @103-104 "A", name: @146-147 "A",
vars: [], vars: [],
}, },
typ: @108-110 Apply( typ: @151-153 Apply(
"", "",
"U8", "U8",
[], [],
), ),
derived: Some( derived: Some(
@115-137 Has( @165-187 Implements(
[ [
@116-123 HasAbility { @166-173 ImplementsAbility {
ability: @116-118 Apply( ability: @166-168 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: Some( impls: Some(
@119-123 HasImpls( @169-173 AbilityImpls(
[ [
@120-122 LabelOnly( @170-172 LabelOnly(
@120-122 "eq", @170-172 "eq",
), ),
], ],
), ),
), ),
}, },
@125-136 HasAbility { @175-186 ImplementsAbility {
ability: @125-129 Apply( ability: @175-179 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
impls: Some( impls: Some(
@130-136 HasImpls( @180-186 AbilityImpls(
[ [
@131-135 LabelOnly( @181-185 LabelOnly(
@131-135 "hash", @181-185 "hash",
), ),
], ],
), ),
@ -248,31 +248,31 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @139-140 "A", name: @189-190 "A",
vars: [], vars: [],
}, },
typ: @144-146 Apply( typ: @194-196 Apply(
"", "",
"U8", "U8",
[], [],
), ),
derived: Some( derived: Some(
@151-165 Has( @208-222 Implements(
[ [
@152-164 HasAbility { @209-221 ImplementsAbility {
ability: @152-154 Apply( ability: @209-211 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: Some( impls: Some(
@155-164 HasImpls( @212-221 AbilityImpls(
[ [
@156-158 LabelOnly( @213-215 LabelOnly(
@156-158 "eq", @213-215 "eq",
), ),
@160-163 LabelOnly( @217-220 LabelOnly(
@160-163 "eq1", @217-220 "eq1",
), ),
], ],
), ),
@ -284,38 +284,38 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @167-168 "A", name: @224-225 "A",
vars: [], vars: [],
}, },
typ: @172-174 Apply( typ: @229-231 Apply(
"", "",
"U8", "U8",
[], [],
), ),
derived: Some( derived: Some(
@179-199 Has( @243-263 Implements(
[ [
@180-192 HasAbility { @244-256 ImplementsAbility {
ability: @180-182 Apply( ability: @244-246 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: Some( impls: Some(
@183-192 HasImpls( @247-256 AbilityImpls(
[ [
@184-186 LabelOnly( @248-250 LabelOnly(
@184-186 "eq", @248-250 "eq",
), ),
@188-191 LabelOnly( @252-255 LabelOnly(
@188-191 "eq1", @252-255 "eq1",
), ),
], ],
), ),
), ),
}, },
@194-198 HasAbility { @258-262 ImplementsAbility {
ability: @194-198 Apply( ability: @258-262 Apply(
"", "",
"Hash", "Hash",
[], [],
@ -328,39 +328,39 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @201-202 "A", name: @265-266 "A",
vars: [], vars: [],
}, },
typ: @206-208 Apply( typ: @270-272 Apply(
"", "",
"U8", "U8",
[], [],
), ),
derived: Some( derived: Some(
@213-233 Has( @284-304 Implements(
[ [
@214-218 HasAbility { @285-289 ImplementsAbility {
ability: @214-218 Apply( ability: @285-289 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
impls: None, impls: None,
}, },
@220-232 HasAbility { @291-303 ImplementsAbility {
ability: @220-222 Apply( ability: @291-293 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: Some( impls: Some(
@223-232 HasImpls( @294-303 AbilityImpls(
[ [
@224-226 LabelOnly( @295-297 LabelOnly(
@224-226 "eq", @295-297 "eq",
), ),
@228-231 LabelOnly( @299-302 LabelOnly(
@228-231 "eq1", @299-302 "eq1",
), ),
], ],
), ),
@ -372,34 +372,34 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @235-236 "A", name: @306-307 "A",
vars: [], vars: [],
}, },
typ: @240-242 Apply( typ: @311-313 Apply(
"", "",
"U8", "U8",
[], [],
), ),
derived: Some( derived: Some(
@247-249 Has( @325-327 Implements(
[], [],
), ),
), ),
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @251-252 "A", name: @329-330 "A",
vars: [], vars: [],
}, },
typ: @256-271 Where( typ: @334-360 Where(
@256-257 BoundVariable( @334-335 BoundVariable(
"a", "a",
), ),
[ [
@260-271 HasClause { @342-360 ImplementsClause {
var: @260-261 "a", var: @342-343 "a",
abilities: [ abilities: [
@266-271 Apply( @355-360 Apply(
"", "",
"Other", "Other",
[], [],
@ -409,36 +409,36 @@ Defs(
], ],
), ),
derived: Some( derived: Some(
@281-303 SpaceBefore( @377-399 SpaceBefore(
Has( Implements(
[ [
@282-289 HasAbility { @378-385 ImplementsAbility {
ability: @282-284 Apply( ability: @378-380 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: Some( impls: Some(
@285-289 HasImpls( @381-385 AbilityImpls(
[ [
@286-288 LabelOnly( @382-384 LabelOnly(
@286-288 "eq", @382-384 "eq",
), ),
], ],
), ),
), ),
}, },
@291-302 HasAbility { @387-398 ImplementsAbility {
ability: @291-295 Apply( ability: @387-391 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
impls: Some( impls: Some(
@296-302 HasImpls( @392-398 AbilityImpls(
[ [
@297-301 LabelOnly( @393-397 LabelOnly(
@297-301 "hash", @393-397 "hash",
), ),
], ],
), ),
@ -454,25 +454,25 @@ Defs(
}, },
Opaque { Opaque {
header: TypeHeader { header: TypeHeader {
name: @305-306 "A", name: @401-402 "A",
vars: [], vars: [],
}, },
typ: @310-312 Apply( typ: @406-408 Apply(
"", "",
"U8", "U8",
[], [],
), ),
derived: Some( derived: Some(
@317-324 Has( @420-427 Implements(
[ [
@318-323 HasAbility { @421-426 ImplementsAbility {
ability: @318-320 Apply( ability: @421-423 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
impls: Some( impls: Some(
@321-323 HasImpls( @424-426 AbilityImpls(
[], [],
), ),
), ),
@ -484,7 +484,7 @@ Defs(
], ],
value_defs: [], value_defs: [],
}, },
@326-327 SpaceBefore( @429-430 SpaceBefore(
Num( Num(
"0", "0",
), ),

View file

@ -1,23 +1,23 @@
A := U8 has [Eq, Hash] A := U8 implements [Eq, Hash]
A := a | a has Other has [Eq, Hash] A := a where a implements Other implements [Eq, Hash]
A := a | a has Other A := a where a implements Other
has [Eq, Hash] implements [Eq, Hash]
A := U8 has [Eq {eq}, Hash {hash}] A := U8 implements [Eq {eq}, Hash {hash}]
A := U8 has [Eq {eq, eq1}] A := U8 implements [Eq {eq, eq1}]
A := U8 has [Eq {eq, eq1}, Hash] A := U8 implements [Eq {eq, eq1}, Hash]
A := U8 has [Hash, Eq {eq, eq1}] A := U8 implements [Hash, Eq {eq, eq1}]
A := U8 has [] A := U8 implements []
A := a | a has Other A := a where a implements Other
has [Eq {eq}, Hash {hash}] implements [Eq {eq}, Hash {hash}]
A := U8 has [Eq {}] A := U8 implements [Eq {}]
0 0

View file

@ -4,7 +4,7 @@ Defs(
Index(2147483648), Index(2147483648),
], ],
regions: [ regions: [
@0-27, @0-38,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,7 +19,7 @@ Defs(
@0-1 Identifier( @0-1 Identifier(
"f", "f",
), ),
@4-27 Where( @4-38 Where(
@4-16 Function( @4-16 Function(
[ [
@4-5 BoundVariable( @4-5 BoundVariable(
@ -38,10 +38,10 @@ Defs(
), ),
), ),
[ [
@20-27 HasClause { @24-38 ImplementsClause {
var: @20-21 "a", var: @24-25 "a",
abilities: [ abilities: [
@26-27 Apply( @37-38 Apply(
"", "",
"A", "A",
[], [],
@ -53,7 +53,7 @@ Defs(
), ),
], ],
}, },
@29-30 SpaceBefore( @40-41 SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "f", ident: "f",

View file

@ -1,3 +1,3 @@
f : a -> (b -> c) | a has A f : a -> (b -> c) where a implements A
f f

View file

@ -1,5 +1,5 @@
f : a -> b | a has Hash & Eq, b has Eq & Hash & Display f : a -> b where a implements Hash & Eq, b implements Eq & Hash & Display
f : a -> b | a has Hash & Eq, b has Hash & Display & Eq f : a -> b where a implements Hash & Eq, b implements Hash & Display & Eq
f f

View file

@ -5,8 +5,8 @@ Defs(
Index(2147483649), Index(2147483649),
], ],
regions: [ regions: [
@0-55, @0-73,
@57-118, @75-154,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -26,7 +26,7 @@ Defs(
@0-1 Identifier( @0-1 Identifier(
"f", "f",
), ),
@4-55 Where( @4-73 Where(
@4-10 Function( @4-10 Function(
[ [
@4-5 BoundVariable( @4-5 BoundVariable(
@ -38,35 +38,35 @@ Defs(
), ),
), ),
[ [
@13-28 HasClause { @17-39 ImplementsClause {
var: @13-14 "a", var: @17-18 "a",
abilities: [ abilities: [
@19-23 Apply( @30-34 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
@26-28 Apply( @37-39 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
], ],
}, },
@30-55 HasClause { @41-73 ImplementsClause {
var: @30-31 "b", var: @41-42 "b",
abilities: [ abilities: [
@36-38 Apply( @54-56 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
@41-45 Apply( @59-63 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
@48-55 Apply( @66-73 Apply(
"", "",
"Display", "Display",
[], [],
@ -77,18 +77,18 @@ Defs(
), ),
), ),
Annotation( Annotation(
@57-58 Identifier( @75-76 Identifier(
"f", "f",
), ),
@61-118 Where( @79-154 Where(
@61-67 SpaceAfter( @79-85 SpaceAfter(
Function( Function(
[ [
@61-62 BoundVariable( @79-80 BoundVariable(
"a", "a",
), ),
], ],
@66-67 BoundVariable( @84-85 BoundVariable(
"b", "b",
), ),
), ),
@ -97,40 +97,40 @@ Defs(
], ],
), ),
[ [
@72-87 HasClause { @94-116 ImplementsClause {
var: @72-73 "a", var: @94-95 "a",
abilities: [ abilities: [
@78-82 Apply( @107-111 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
@85-87 Apply( @114-116 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
], ],
}, },
@93-118 HasClause { @122-154 ImplementsClause {
var: @93-94 SpaceBefore( var: @122-123 SpaceBefore(
"b", "b",
[ [
Newline, Newline,
], ],
), ),
abilities: [ abilities: [
@99-103 Apply( @135-139 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
@106-113 Apply( @142-149 Apply(
"", "",
"Display", "Display",
[], [],
), ),
@116-118 Apply( @152-154 Apply(
"", "",
"Eq", "Eq",
[], [],
@ -142,7 +142,7 @@ Defs(
), ),
], ],
}, },
@120-121 SpaceBefore( @156-157 SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "f", ident: "f",

View file

@ -1,7 +1,7 @@
f : a -> b | a has Hash & Eq, b has Eq & Hash & Display f : a -> b where a implements Hash & Eq, b implements Eq & Hash & Display
f : a -> b f : a -> b
| a has Hash & Eq, where a implements Hash & Eq,
b has Hash & Display & Eq b implements Hash & Display & Eq
f f

View file

@ -4,7 +4,7 @@ Defs(
Index(2147483648), Index(2147483648),
], ],
regions: [ regions: [
@0-48, @0-73,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,7 +19,7 @@ Defs(
@0-1 Identifier( @0-1 Identifier(
"f", "f",
), ),
@4-48 Where( @4-73 Where(
@4-16 Function( @4-16 Function(
[ [
@4-5 BoundVariable( @4-5 BoundVariable(
@ -38,30 +38,30 @@ Defs(
), ),
), ),
[ [
@20-27 HasClause { @24-38 ImplementsClause {
var: @20-21 "a", var: @24-25 "a",
abilities: [ abilities: [
@26-27 Apply( @37-38 Apply(
"", "",
"A", "A",
[], [],
), ),
], ],
}, },
@29-37 HasClause { @40-55 ImplementsClause {
var: @29-30 "b", var: @40-41 "b",
abilities: [ abilities: [
@35-37 Apply( @53-55 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
], ],
}, },
@39-48 HasClause { @57-73 ImplementsClause {
var: @39-40 "c", var: @57-58 "c",
abilities: [ abilities: [
@45-48 Apply( @70-73 Apply(
"", "",
"Ord", "Ord",
[], [],
@ -73,7 +73,7 @@ Defs(
), ),
], ],
}, },
@50-51 SpaceBefore( @75-76 SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "f", ident: "f",

View file

@ -1,3 +1,3 @@
f : a -> (b -> c) | a has A, b has Eq, c has Ord f : a -> (b -> c) where a implements A, b implements Eq, c implements Ord
f f

View file

@ -1,3 +1,3 @@
f : a -> (b -> c) | a has Hash, b has Eq, c has Ord f : a -> (b -> c) where a implements Hash, b implements Eq, c implements Ord
f f

View file

@ -4,7 +4,7 @@ Defs(
Index(2147483648), Index(2147483648),
], ],
regions: [ regions: [
@0-67, @0-92,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,7 +19,7 @@ Defs(
@0-1 Identifier( @0-1 Identifier(
"f", "f",
), ),
@4-67 Where( @4-92 Where(
@4-16 SpaceAfter( @4-16 SpaceAfter(
Function( Function(
[ [
@ -43,40 +43,40 @@ Defs(
], ],
), ),
[ [
@24-34 HasClause { @28-45 ImplementsClause {
var: @24-25 "a", var: @28-29 "a",
abilities: [ abilities: [
@30-34 Apply( @41-45 Apply(
"", "",
"Hash", "Hash",
[], [],
), ),
], ],
}, },
@42-50 HasClause { @53-68 ImplementsClause {
var: @42-43 SpaceBefore( var: @53-54 SpaceBefore(
"b", "b",
[ [
Newline, Newline,
], ],
), ),
abilities: [ abilities: [
@48-50 Apply( @66-68 Apply(
"", "",
"Eq", "Eq",
[], [],
), ),
], ],
}, },
@58-67 HasClause { @76-92 ImplementsClause {
var: @58-59 SpaceBefore( var: @76-77 SpaceBefore(
"c", "c",
[ [
Newline, Newline,
], ],
), ),
abilities: [ abilities: [
@64-67 Apply( @89-92 Apply(
"", "",
"Ord", "Ord",
[], [],
@ -88,7 +88,7 @@ Defs(
), ),
], ],
}, },
@69-70 SpaceBefore( @94-95 SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "f", ident: "f",

View file

@ -1,6 +1,6 @@
f : a -> (b -> c) f : a -> (b -> c)
| a has Hash, where a implements Hash,
b has Eq, b implements Eq,
c has Ord c implements Ord
f f

View file

@ -4,7 +4,7 @@ Defs(
Index(2147483648), Index(2147483648),
], ],
regions: [ regions: [
@0-15, @0-26,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,15 +19,15 @@ Defs(
@0-1 Identifier( @0-1 Identifier(
"f", "f",
), ),
@4-15 Where( @4-26 Where(
@4-5 BoundVariable( @4-5 BoundVariable(
"a", "a",
), ),
[ [
@8-15 HasClause { @12-26 ImplementsClause {
var: @8-9 "a", var: @12-13 "a",
abilities: [ abilities: [
@14-15 Apply( @25-26 Apply(
"", "",
"A", "A",
[], [],
@ -39,7 +39,7 @@ Defs(
), ),
], ],
}, },
@17-18 SpaceBefore( @28-29 SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "f", ident: "f",

View file

@ -1,3 +1,3 @@
f : a | a has A f : a where a implements A
f f

View file

@ -1,3 +1,3 @@
f : a -> U64 | a has Hash f : a -> U64 where a implements Hash
f f

View file

@ -4,7 +4,7 @@ Defs(
Index(2147483648), Index(2147483648),
], ],
regions: [ regions: [
@0-29, @0-40,
], ],
space_before: [ space_before: [
Slice(start = 0, length = 0), Slice(start = 0, length = 0),
@ -19,7 +19,7 @@ Defs(
@0-1 Identifier( @0-1 Identifier(
"f", "f",
), ),
@4-29 Where( @4-40 Where(
@4-12 SpaceAfter( @4-12 SpaceAfter(
Function( Function(
[ [
@ -38,10 +38,10 @@ Defs(
], ],
), ),
[ [
@19-29 HasClause { @23-40 ImplementsClause {
var: @19-20 "a", var: @23-24 "a",
abilities: [ abilities: [
@25-29 Apply( @36-40 Apply(
"", "",
"Hash", "Hash",
[], [],
@ -53,7 +53,7 @@ Defs(
), ),
], ],
}, },
@31-32 SpaceBefore( @42-43 SpaceBefore(
Var { Var {
module_name: "", module_name: "",
ident: "f", ident: "f",

View file

@ -1,4 +1,4 @@
f : a -> U64 f : a -> U64
| a has Hash where a implements Hash
f f

View file

@ -5421,10 +5421,10 @@ mod test_fmt {
} }
#[test] #[test]
fn opaque_has_clause() { fn opaque_implements_clause() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
A := U8 has [Eq, Hash] A := U8 implements [Eq, Hash]
0 0
"# "#
@ -5435,7 +5435,7 @@ mod test_fmt {
r#" r#"
A := A :=
U8 U8
has [Eq, Hash] implements [Eq, Hash]
0 0
"# "#
@ -5443,7 +5443,7 @@ mod test_fmt {
indoc!( indoc!(
r#" r#"
A := U8 A := U8
has [Eq, Hash] implements [Eq, Hash]
0 0
"# "#
@ -5453,15 +5453,15 @@ mod test_fmt {
expr_formats_to( expr_formats_to(
indoc!( indoc!(
r#" r#"
A := a | a has Hash has [ Eq, Hash ] A := a where a implements Hash implements [ Eq, Hash ]
0 0
"# "#
), ),
indoc!( indoc!(
r#" r#"
A := a | a has Hash A := a where a implements Hash
has [Eq, Hash] implements [Eq, Hash]
0 0
"# "#
@ -5471,14 +5471,14 @@ mod test_fmt {
expr_formats_to( expr_formats_to(
indoc!( indoc!(
r#" r#"
A := U8 has [] A := U8 implements []
0 0
"# "#
), ),
indoc!( indoc!(
r#" r#"
A := U8 has [] A := U8 implements []
0 0
"# "#
@ -5518,10 +5518,10 @@ mod test_fmt {
} }
#[test] #[test]
fn opaque_has_with_impls() { fn opaque_implements_with_impls() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
A := U8 has [Eq { eq }, Hash { hash }] A := U8 implements [Eq { eq }, Hash { hash }]
0 0
"# "#
@ -5529,7 +5529,7 @@ mod test_fmt {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
A := U8 has [Eq { eq, eq1 }] A := U8 implements [Eq { eq, eq1 }]
0 0
"# "#
@ -5538,8 +5538,8 @@ mod test_fmt {
expr_formats_to( expr_formats_to(
indoc!( indoc!(
r#" r#"
A := U8 has [Eq { eq, eq1 }] A := U8 implements [Eq { eq, eq1 }]
A := U8 has [Eq { A := U8 implements [Eq {
eq, eq,
eq1 eq1
}] }]
@ -5549,8 +5549,8 @@ mod test_fmt {
), ),
indoc!( indoc!(
r#" r#"
A := U8 has [Eq { eq, eq1 }] A := U8 implements [Eq { eq, eq1 }]
A := U8 has [ A := U8 implements [
Eq { Eq {
eq, eq,
eq1, eq1,
@ -5564,8 +5564,8 @@ mod test_fmt {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
A := a | a has Other A := a where a implements Other
has [Eq { eq }, Hash { hash }] implements [Eq { eq }, Hash { hash }]
0 0
"# "#
@ -5573,7 +5573,7 @@ mod test_fmt {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
A := U8 has [Eq {}] A := U8 implements [Eq {}]
0 0
"# "#
@ -5621,7 +5621,7 @@ mod test_fmt {
dataIndices : List Nat, dataIndices : List Nat,
data : List (T k v), data : List (T k v),
size : Nat, size : Nat,
} | k has Hash & Eq } where k implements Hash & Eq
a a
"# "#
@ -5833,12 +5833,12 @@ mod test_fmt {
r#" r#"
interface Foo exposes [] imports [] interface Foo exposes [] imports []
A has A implements
## This is member ab ## This is member ab
ab : a -> a | a has A ab : a -> a where a implements A
## This is member de ## This is member de
de : a -> a | a has A de : a -> a where a implements A
f = g f = g
"# "#
@ -5880,7 +5880,7 @@ mod test_fmt {
fn clauses_with_multiple_abilities() { fn clauses_with_multiple_abilities() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
f : {} -> a | a has Eq & Hash & Decode f : {} -> a where a implements Eq & Hash & Decode
f f
"# "#
@ -5889,8 +5889,8 @@ mod test_fmt {
expr_formats_to( expr_formats_to(
indoc!( indoc!(
r#" r#"
f : {} -> a | a has Eq & Hash & Decode, f : {} -> a where a implements Eq & Hash & Decode,
b has Eq & Hash b implements Eq & Hash
f f
"# "#
@ -5898,10 +5898,10 @@ mod test_fmt {
indoc!( indoc!(
// TODO: ideally, this would look a bit nicer - consider // TODO: ideally, this would look a bit nicer - consider
// f : {} -> a // f : {} -> a
// | a has Eq & Hash & Decode, // where a implements Eq & Hash & Decode,
// b has Eq & Hash // b implements Eq & Hash
r#" r#"
f : {} -> a | a has Eq & Hash & Decode, b has Eq & Hash f : {} -> a where a implements Eq & Hash & Decode, b implements Eq & Hash
f f
"# "#

View file

@ -14,6 +14,7 @@ roc_error_macros = { path = "../../error_macros" }
roc_module = { path = "../module" } roc_module = { path = "../module" }
roc_region = { path = "../region" } roc_region = { path = "../region" }
roc_serialize = { path = "../serialize" } roc_serialize = { path = "../serialize" }
roc_parse = { path = "../parse" }
ven_pretty = { path = "../../vendor/pretty" } ven_pretty = { path = "../../vendor/pretty" }

View file

@ -136,8 +136,8 @@ fn find_names_needed(
if !root_appearances.contains_key(&root) { if !root_appearances.contains_key(&root) {
roots.push(root); roots.push(root);
} }
// Able vars are always printed at least twice (in the signature, and in the "has" // Able vars are always printed at least twice (in the signature, and in the
// clause set). // "implements" clause set).
root_appearances.insert(root, Appearances::Multiple); root_appearances.insert(root, Appearances::Multiple);
} }
RecursionVar { RecursionVar {
@ -606,9 +606,16 @@ fn variable_to_string(
ctx.able_variables.sort(); ctx.able_variables.sort();
ctx.able_variables.dedup(); ctx.able_variables.dedup();
for (i, (var, abilities)) in ctx.able_variables.into_iter().enumerate() { for (i, (var, abilities)) in ctx.able_variables.into_iter().enumerate() {
buf.push_str(if i == 0 { " | " } else { ", " }); if i == 0 {
buf.push(' ');
buf.push_str(roc_parse::keyword::WHERE)
} else {
buf.push(',');
}
buf.push(' ');
buf.push_str(var); buf.push_str(var);
buf.push_str(" has"); buf.push(' ');
buf.push_str(roc_parse::keyword::IMPLEMENTS);
for (i, ability) in abilities.into_sorted_iter().enumerate() { for (i, ability) in abilities.into_sorted_iter().enumerate() {
if i > 0 { if i > 0 {
buf.push_str(" &"); buf.push_str(" &");

View file

@ -2381,7 +2381,7 @@ pub enum Content {
/// This can only happen when unified with a [Self::RigidAbleVar]. /// This can only happen when unified with a [Self::RigidAbleVar].
FlexAbleVar(Option<SubsIndex<Lowercase>>, SubsSlice<Symbol>), FlexAbleVar(Option<SubsIndex<Lowercase>>, SubsSlice<Symbol>),
/// Like a [Self::RigidVar], but is also bound to 1+ abilities. /// Like a [Self::RigidVar], but is also bound to 1+ abilities.
/// For example, "a has Hash". /// For example, "a implements Hash".
RigidAbleVar(SubsIndex<Lowercase>, SubsSlice<Symbol>), RigidAbleVar(SubsIndex<Lowercase>, SubsSlice<Symbol>),
/// name given to a recursion variable /// name given to a recursion variable
RecursionVar { RecursionVar {

View file

@ -1687,7 +1687,7 @@ pub enum Type {
} }
/// A lambda set under an arrow in a ability member signature. For example, in /// A lambda set under an arrow in a ability member signature. For example, in
/// Default has default : {} -> a | a has Default /// Default has default : {} -> a where a implements Default
/// the unspecialized lambda set for the arrow "{} -> a" would be `a:default:1`. /// the unspecialized lambda set for the arrow "{} -> a" would be `a:default:1`.
/// ///
/// Lambda sets in member signatures are never known until those members are specialized at a /// Lambda sets in member signatures are never known until those members are specialized at a
@ -3819,7 +3819,7 @@ fn write_debug_error_type_help(error_type: ErrorType, buf: &mut String, parens:
buf.push('('); buf.push('(');
} }
buf.push_str(name.as_str()); buf.push_str(name.as_str());
write!(buf, "has {symbol:?}").unwrap(); write!(buf, "{} {:?}", roc_parse::keyword::IMPLEMENTS, symbol).unwrap();
if write_parens { if write_parens {
buf.push(')'); buf.push(')');
} }

View file

@ -1,7 +1,7 @@
# +opt infer:print_only_under_alias # +opt infer:print_only_under_alias
app "test" provides [main] to "./platform" app "test" provides [main] to "./platform"
F a : a | a has Hash F a : a where a implements Hash
main : F a -> F a main : F a -> F a
#^^^^{-1} a -[[main(0)]]-> a | a has Hash #^^^^{-1} a -[[main(0)]]-> a where a implements Hash

Some files were not shown because too many files have changed in this diff Show more