candy-lang / candy Goto Github PK
View Code? Open in Web Editor NEW๐ญ A sweet, functional programming language that is robust, minimalistic, and expressive.
License: MIT License
๐ญ A sweet, functional programming language that is robust, minimalistic, and expressive.
License: MIT License
Describe the bug
#390 introduced a bug where the module resolution inside packages fails to detect if a file is located in the package on Windows.
The underlying problem is the platform specific behavior of fs::canonicalize
(see the documentation). This function returns a UNC-Path prefixed with \\?\
while package.to_path
returns a DOS-Path without the prefix. strip_prefix
then returns an error and the compiler thinks the file is not located in the package.
The dunce crate was created to combat this issue, but converting the UNC-path to a URL and back to a file path seems to be a quick workaround.
let canonicalized = Url::from_file_path(canonicalized)
.expect("Invalid file path.")
.to_file_path()
.expect("File path conversion error.");
Track the precedence to avoid superfluous parentheses around a tag's value
Ideas:
Builtin print should no longer be needed once we have robust stdio and can visualize traces.
Allow constant evaluation to create tags in the MIR
Currently, the formatter formats this code:
foo a :=
# a comment
bar
into this:
foo a := # a comment
bar
While that is (not, #485) valid, I think the code shouldn't be formatted.
Create packages:
Ideas:
Instead of symbols, we want to have tags that can also have an optional associated value. Our current symbols will just be named tags. Tags without an associated value can be called just like functions to associate a value with them.
tagWithoutValue = Foo
tagWithValue = Foo 3
newResult = Ok 4
error = Error "That didn't work."
dynamicallyCreatedTag = tagWithoutValue 5
nestedTag = Foo (Bar 3)
We fuzz Candy functions, but the fuzz cases can sometimes get large. It would be a better developer experience if we simplify them.
Currently, the formatter takes this code:
is value := value %
Ok _ | Error _ -> True
_ -> False
and turns it into this:
is value := value %
Ok _ | Error _ -> True
_ -> False
This code is invalid though.
Have multiple workers that execute multiple fibers in parallel
For example, in a tree.
Our current CLI is kind of weird โ you have to specify a .candy
file including the extension and we just assume that the current working directory is the project. I want to look into optimizations (and in particular specifying custom optimization guidance using a scoring function) next.
Just to recap the distinction of packages vs. modules, I believe this is our current definition:
use
.I thought a bit about how Candy packages could work and how they could interact with our CLI.
My proposal on how to distinguishing packages from modules: Packages can only be folders and next to the top-level _.candy
file, they must also contain a package.candy
file containing information on the package itself. Here's what a minimal package might look like:
foo/
_.candy
package.candy
When invoking the CLI, it will look if the current working directory is a package. If it isn't it will walk up the hierarchy and stop either if it finds a package.candy
file or it doesn't find a _.candy
file indicating a folder is a Candy module.
We probably also want to allow specifying packages that are not the current working directory, e.g. like this: candy --package path/to/package/folder build
.
The big remaining question is what the package.candy
file contains. This depends on what you can do with the package. Later on, packages can be built into binaries or published for others. For now, the package.candy
could contain information on how to optimize the module using a custom scoring function. For example, if we want to tell the MIR optimizer to optimize the Fibonacci program so that the binary executes fibonacci 610
as fast as possible, we might have the following files:
# _.candy
[int, list] = use "Core"
fibonacci n =
...
main environment :=
n = environment.args | list.get 0 | int.parse
fibonacci n | print
# package.candy (for me, GitHub hides it but you may have to scroll in this code section)
[int] = use "Core"
# Executed when running `candy publish`. The `environment` contains information about the
# dev environment such as the arguments given to the command, environment variables, etc.
publish environment :=
# Assuming tags are supported, this only publishes the `Fibonacci` export from the
# `_.candy` file. Alternatively, you can also use `Everything` to publish the entire
# module. We may want to think about restricting this to export that statically resolve
# to a struct (so that `use SomePublicPackage` is always a struct), but we could also
# think about allowing exporting single values.
[
Value: Only Fibonacci,
Name: "MyPackage",
Description: (use "README.md") | string.parse,
Version: 3,
...
]
# Executed when running `candy build`.
build environment :=
[
EntryPoint: Main,
# A custom scoring function where lower scores are better.
OptimizationScoring: { artifact ->
int.add
artifact.numInstructions
artifact.run Main 610
},
Plugins: [
Stdin,
Stdout,
Variables,
Random,
HttpsServer,
],
Target: Wasm,
]
This way, we can completely customize the build using arguments (thereby replacing things such as Flutter flavors).
I think we should support these commands:
candy analyze
: Builds the project (maybe up until the HIR?) and reports all errors.candy build
: Builds the project according to the build function in the package.candy
.candy run
: Builds the project and runs it with the given arguments.For simple scripts, we may want to allow standalone modules that can be run independently. I'd suggest these are treated similarly to being the sole content of a package with a default build
configuration.
candy --file my/Downloads/something.candy run
Because these are self-contained, they can't import any other files โ to do that, you have to first create a package.
Perhaps a bit unrelated, I think it would also be useful to have the ability to run arbitrary functions from the command line.
candy --function examples/fibonacci.candy:fibonacci run 610
Hey guys,
I am looking quite excited at your 'new' programming language!
I just wanted to know if you use or have a grammar file for these Candy language? That grammar file could (!) be used to make Candy as an interpreted language as well.
Some ideas:
In the MIR, all information about where functions come from is lost. We should keep that info around โ if not for every expression, at least for every closure. This would also enable showing stack traces in code not compiled with tracing.
While we do have fuzzing for the Core library, a few simple unit tests would also go a long way for catching obvious bugs.
Hey guys,
I am quite more excited since the last issue! It`s fantastic that candy got it first release!!!! ๐ YEHA! ๐ฏ
I looked into the tools you provided with the first release and noticed that these tools are just for windows. Is it possible to provide these tools for linux and/or macosx in the next releases?
Btw your extension for visual studio code works just as expected, excellent!
Again, thank you very much for you work!
Currently, foo 0 | (bar 1)
first evaluates bar 1
, then foo 0
, and then calls the result of bar 1
with the result of foo 0
. This is because pipes are not preserved in the AST, instead the expression gets lowered to the equivalent of (bar 1) (foo 0)
.
We should keep pipe expressions in the AST and then fix the AST to HIR lowering so that the above code evaluates foo 0
, then bar 1
, and then calls the result of bar 1
with the result of foo 0
.
Describe the bug
main := { _ ->
foo = 123 # Rename doesn't work
โจ.print foo # Rename works
}
foo
bar | baz
## Currently, this is parsed as `baz (foo bar)`.
Some ideas about optimizations we could implement:
Think about and implement visualizing Candy function executions.
Currently, running candy fuzz
doesn't show stack traces because that would cause segfaults. We should fix that.
Delete a bunch of files!
If functions were responsible for fulfilling the needs of functions given to them as arguments, every higher-order function would have to include a try
block or something similar and then re-propagate that responsibility to the caller.
That's why we should establish the rule that inside higher-order functions, you shouldn't have to care about the needs of functions given as parameters. Instead, the caller is responsible for giving you only functions that accept the values that you call them with.
Semantically, this would equate to wrapping functions in closures.
bar a = ...
# A call to higher-order function foo:
foo bar
# Would really be equivalent to this:
foo { a -> bar a }
Currently, the parser doesn't parse this code:
is value := # โจ.print "is result?"
value %
Ok _ | Error _ -> True
_ -> False
It complains that value
is not defined, so it seems like it stops parsing the function after the first line.
[Foo, 1, {a}] = [Foo, 2, {A: B]]
could generate a message like Expected `[_, 1, _]`, got `[_, 2, _]`.
When checking individual cases of a match, we don't want to construct the error messages eagerly. For the vast majority of cases, at least one case will match, so concatenating (and thereby allocating) lots of strings slows down the code by a lot.
Instead, when no case matches, we should revisit all the cases and build a nice error message, detailing why each case doesn't match.
Hi, future me! Past me here :)
For performance, we take out building the error messages when cases don't match. We should revisit this in the future. Our current state where we build error messages for non-matching cases: a68dbb9
Describe the bug
foo =
bar = 123
Gets formatted to the following, invalid code:
foo = bar = 123
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.