Skip to content

Commit

Permalink
Closes #257 roadmap (#277)
Browse files Browse the repository at this point in the history
* roadmap

* Update BASIC_TYPES.md
  • Loading branch information
wende authored Feb 3, 2018
1 parent 26c9443 commit 90d53cf
Show file tree
Hide file tree
Showing 9 changed files with 436 additions and 0 deletions.
27 changes: 27 additions & 0 deletions roadmap/BASIC_TYPES.md
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())}` |
53 changes: 53 additions & 0 deletions roadmap/COMMENTS.md
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
"""
```
60 changes: 60 additions & 0 deletions roadmap/FUNCTIONS.md
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
58 changes: 58 additions & 0 deletions roadmap/INTEROP.md
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.
42 changes: 42 additions & 0 deletions roadmap/MODULES.md
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.
46 changes: 46 additions & 0 deletions roadmap/README.md
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
58 changes: 58 additions & 0 deletions roadmap/STRUCTURES.md
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.
Loading

0 comments on commit 90d53cf

Please sign in to comment.