-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* roadmap * Update BASIC_TYPES.md
- Loading branch information
Showing
9 changed files
with
436 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
# Basic types | ||
|
||
|
||
Because Elm and Elixir share a lot of common basic types, there is no need to redefine all of them for Elchemy need. For simplicity and interoperability some of the standard types translate directly to each other. | ||
|
||
Here is a table of all standard types used in Elchemy environment and their Elixir equivalents: | ||
|
||
| Elchemy | Elixir | | ||
| --- | --- | | ||
| `a` | `any()` | ||
| `comparable` | `term()` | ||
| `Int` | `integer()` | ||
| `Float` | `number()` | ||
| `number` | `number()` | ||
| `Bool` | `boolean()` | ||
| `Char` | `integer()` | ||
| `String` | `String.t()` | ||
| `List x` | `list()` | ||
| `(1, 2)` | `{1, 2}` | ||
| `Maybe Int` | `{integer()}` | `nil` | ||
| `Just x` | `{x}` | ||
| `Nothing` | `nil` | ||
| `Result x y` | `{:ok, y}` | `{:error, x}` | ||
| `Ok x` | `{:ok, x}` | ||
| `Err x` | `{:error, x}` | ||
| `{x = 1, y = 1}` | `%{x: 1, y: y}` | | ||
| `Dict String Int` | `%{key(String.t()) => integer())}` | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
# Comments | ||
|
||
In Elchemy there is several types of comments. | ||
|
||
### Inline comments | ||
```elm | ||
-- My comment | ||
``` | ||
|
||
Which are always ignored by the compiler and won't be included in the compiled output file | ||
|
||
### Block comment | ||
|
||
Second type of a comment is a block comment | ||
|
||
``` elm | ||
{- block comment -} | ||
``` | ||
|
||
Which are included in the output file, but can only be used as a top level construct | ||
|
||
### Doc comments | ||
Another one and probably most important is a doc-comment. | ||
|
||
``` elm | ||
{-| Doc coment -} | ||
``` | ||
Doc-comments follow these rules: | ||
|
||
1. **First doc comment in a file always compiles to a `@moduledoc`** | ||
2. Doc comments before types compile to `@typedoc` | ||
3. Doc comments before functions compile to `@doc` | ||
4. A doc comment with 4 space indentation and containing a `==` comparison in it will be compiled to a **one line** [doctest](https://elixir-lang.org/getting-started/mix-otp/docs-tests-and-with.html#doctests) | ||
|
||
Doctest example: | ||
|
||
```elm | ||
{-| My function adds two values | ||
myFunction 1 2 == 3 | ||
-} | ||
``` | ||
|
||
Would end up being | ||
|
||
```elixir | ||
@doc """ | ||
My function adds two values | ||
iex> my_function().(1).(2) | ||
3 | ||
""" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# Function definition and currying | ||
|
||
### Function definition | ||
To define a function that gets exported you **need** to declare a type for it with | ||
|
||
``` elm | ||
functionName : ArgType -> ArgType2 -> ReturnType | ||
``` | ||
|
||
And a function body underneath | ||
|
||
``` elm | ||
functionName argOne argTwo = body | ||
``` | ||
|
||
This will output following definition: | ||
|
||
``` elixir | ||
curry function_name/2 | ||
@spec function_name(arg_type, arg_type2) :: return_type | ||
def function_name(arg_one, arg_two) do | ||
body | ||
end | ||
``` | ||
|
||
Following rules apply: | ||
|
||
1. A function will use `def` or `defp` based on `exposing` clause at the top of the module | ||
2. The name of the function as well as its spec will always be snake_cased version of the camelCase name | ||
3. `curry function/arity` is a construct that makes the function redefined in a form that takes 0 arguments, and returns X times curried function (2 times in this example). Which means that from elixir our function can be called both as: `function_name(arg_one, arg_two)` or `function_name().(arg_one).(arg_two)` and it won't have any different effect | ||
4. `@spec` clause will always resolve types provided, to a most readable and still understandable by the elixir compiler form | ||
|
||
#### Curried definition | ||
Because of the curried nature of Elm function definitions we can just make our function return functions | ||
|
||
For example instead of writing | ||
|
||
``` elm | ||
addTwo : Int -> Int | ||
addTwo a = 2 + a | ||
``` | ||
|
||
We could just write | ||
|
||
``` elm | ||
addTwo : Int -> Int | ||
addTwo = (+) 2 | ||
``` | ||
|
||
In which case Elchemy will recognize a curried return and still provide you with a 1 and 0 arity functions, not only the 0 one. | ||
And output of such a definition would look like: | ||
|
||
``` elixir | ||
curry add_two/1 | ||
@spec add_two(integer) :: integer | ||
def add_two(x1) do | ||
(&+/0).(2).(x1) | ||
end | ||
``` | ||
Which basically means that Elchemy derives function arity from the type, rathar then function body |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Interop | ||
|
||
|
||
## Calling Elchemy | ||
|
||
To call generated code from Elchemy you don't need anything special. | ||
Each function exposed from a module can be called either in it's regular or curried form. | ||
|
||
Keep in mind that in case of functions expecting a [higher-order function\[s\]](https://en.wikipedia.org/wiki/Higher-order_function) in its parameters Elchemy will also do it's magic to make sure it's compatible both ways. | ||
|
||
For instance if your function looks like that: | ||
```elm | ||
applyFunction2 : (a -> b -> c) -> a -> b -> c | ||
applyFunction2 f p1 p2 = f p1 p2 | ||
``` | ||
|
||
You can call it Elixir way: | ||
```elixir | ||
apply_function2(fn a, b -> a + b end) | ||
``` | ||
As well as Elchemy (curried) way: | ||
```elixir | ||
apply_function2(fn a -> fn b -> a + b end end) | ||
``` | ||
And it will work as fine | ||
|
||
|
||
--- | ||
## Calling Elixir | ||
|
||
|
||
To call elixir from Elchemy you need to define a foreing function interface. | ||
To do that you can use `ffi` [special syntax (?)](SPECIAL_SYNTAX.md) | ||
|
||
`ffi` requires the function to have a very specific format, which is: | ||
|
||
1. You need to make sure the type signature is adequate to the corresponding typespec of a function | ||
2. There should be no explicitly stated parameters (Defined as `f = ...` not `f a b c = ...`) | ||
3. The **only** expression inside the function should be an ffi call in a format of: | ||
`ffi "Module" "function"` | ||
|
||
A good example of an ffi call would be | ||
|
||
```elm | ||
upcase : String -> String | ||
upcase = | ||
ffi "String" "upcase" | ||
``` | ||
A generated code of that statement would be | ||
|
||
``` elixir | ||
@spec upcase(String.t) :: String.t | ||
curry upcase/1 | ||
verify as: String.upcase/1 | ||
def upcase(a1), do: String.upcase(a1) | ||
``` | ||
|
||
Where `verify, as:` is a type safety generator about which you can read more in [testing](TESTING.md) section. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
# Module definition and imports | ||
|
||
To define a module in Elchemy you need to use a | ||
|
||
``` elm | ||
module ModuleName exposing (..) | ||
``` | ||
|
||
Which would directly translate to `defmodule` block where functions/types mentioned in the `exposing` clause will automatically use `def` or `defp` | ||
|
||
## Imports | ||
|
||
There are two types of imports in Elchemy. | ||
|
||
### Without exposing | ||
One is a import without exposed functions like | ||
|
||
``` elm | ||
import SomeModule | ||
``` | ||
Which would directly translate to | ||
|
||
``` elixir | ||
alias SomeModule | ||
``` | ||
|
||
Because it doesn't import any of the exposed contents, only makes sure | ||
that the module is in our namespace. | ||
|
||
### With exposing | ||
|
||
``` elm | ||
import SomeModule exposing (funA, TypeA, funB) | ||
``` | ||
Which outputs | ||
|
||
``` elixir | ||
import SomeModule, only: [{:fun_a, 0}, {:fun_b, 0}] | ||
``` | ||
|
||
Which would put `SomeModule` into our namespace and also allow us to use | ||
`fun_a` and `fun_b` without explicitly adding a module name before them. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Roadmap | ||
|
||
This folder lists all of the ideas and solutions behind Elchemy to share the | ||
ideology as well as existing and incoming solutions to problems this project faced | ||
or eventually will have to face | ||
|
||
# Table of contents | ||
## Basics | ||
- [Basic Types](BASIC_TYPES.md) | ||
- [Defining Types](TYPES.md) | ||
- [Defining Type Aliases](TYPE_ALIASES.md) | ||
- [Structures](STRUCTURES.md) | ||
- [Defining Modules](MODULES.md) | ||
- [Defining Functions](FUNCTIONS.md) | ||
- [Comments](COMMENTS.md) | ||
- [Interop (Foreign Function Interface)](INTEROP.md) | ||
|
||
# Introduction | ||
|
||
|
||
Elchemy is a set of tools and frameworks, designed to provide a language and an environment | ||
as close to [Elm programming language](http://elm-lang.org) as possible, to build server applications | ||
in a DSL-like manner for Erlang VM platform, with a readable and efficient Elixir code as an output. | ||
|
||
## Features | ||
|
||
Elchemy inherits many values from its parents: Elm and Elixir | ||
|
||
### Elm | ||
- ML like syntax maximizing expressiveness with additional readability and simplicity constraints | ||
- Static typing with type inference | ||
- Beautiful compilation errors | ||
- Tagged union types and type aliases with type parameters (aka generic types) | ||
- All functions are curried by default | ||
- [No typeclasses](http://www.haskellforall.com/2012/05/scrap-your-type-classes.html) | ||
|
||
### Erlang/Elixir | ||
- Documentation as a first class citizen | ||
- Doc tests | ||
- Battle-tested distribution system that just works | ||
|
||
### Additional | ||
- Foreign function calls type saftety | ||
- Foreign function calls purity checks | ||
- Dependency system based on GitHub | ||
- Compile time code optimizations |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# Structs | ||
|
||
Elchemy represents all the structs as maps, so a struct defined like | ||
|
||
``` elm | ||
human : { name : String | ||
, age : Int | ||
} | ||
``` | ||
Is an equivalent of | ||
|
||
``` elixir | ||
@spec human :: %{name: String.t(), age: integer()} | ||
``` | ||
|
||
Also type aliases denoting structs can be instantiated like functions | ||
|
||
``` elm | ||
type alias Human = | ||
{ name : String | ||
, age : Int | ||
} | ||
``` | ||
``` elm | ||
Human "Krzysztof" 22 | ||
``` | ||
|
||
## Struct polymorphism | ||
What's more structs can describe a map that has at least specified elements using an update syntax. | ||
|
||
``` elm | ||
type alias Employee x = | ||
{ x | ||
| salary : Int | ||
} | ||
``` | ||
Which means any struct that has a field `salary` of type integer. | ||
That way we can define our psuedo-inheritance and polymorphism for more advanced structures. | ||
|
||
``` elm | ||
type alias Human = | ||
Employee | ||
{ name : String | ||
, age : Int } | ||
|
||
human : Human | ||
``` | ||
Would resolve to | ||
|
||
``` elixir | ||
@spec human :: %{ | ||
salary: integer(), | ||
name: String.t, | ||
age: integer() | ||
} | ||
``` | ||
|
||
But be advised that using this "polymorhpic" approach strips us from the ability to use type aliases as constructors. |
Oops, something went wrong.