{"id":13546412,"url":"https://github.com/alpaca-lang/alpaca","last_synced_at":"2025-05-16T07:05:21.977Z","repository":{"id":50258129,"uuid":"50557264","full_name":"alpaca-lang/alpaca","owner":"alpaca-lang","description":"Functional programming inspired by ML for the Erlang VM","archived":false,"fork":false,"pushed_at":"2020-06-13T19:19:15.000Z","size":1901,"stargazers_count":1443,"open_issues_count":34,"forks_count":48,"subscribers_count":59,"default_branch":"main","last_synced_at":"2025-04-08T16:09:27.828Z","etag":null,"topics":["alpaca","erlang","erlang-vm","hindley-milner","ml","statically-typed"],"latest_commit_sha":null,"homepage":"","language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alpaca-lang.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog.org","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":"code_of_conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-28T05:10:09.000Z","updated_at":"2025-03-31T08:58:31.000Z","dependencies_parsed_at":"2022-08-25T14:30:28.408Z","dependency_job_id":null,"html_url":"https://github.com/alpaca-lang/alpaca","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaca-lang%2Falpaca","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaca-lang%2Falpaca/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaca-lang%2Falpaca/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alpaca-lang%2Falpaca/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alpaca-lang","download_url":"https://codeload.github.com/alpaca-lang/alpaca/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254485060,"owners_count":22078767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["alpaca","erlang","erlang-vm","hindley-milner","ml","statically-typed"],"created_at":"2024-08-01T12:00:36.778Z","updated_at":"2025-05-16T07:05:21.915Z","avatar_url":"https://github.com/alpaca-lang.png","language":"Erlang","funding_links":[],"categories":["Erlang","Erlang Tools, Libraries, and Frameworks","Tools"],"sub_categories":["E-Books","Mesh networks"],"readme":"Alpaca\n=====\n[![Build Status](https://travis-ci.org/alpaca-lang/alpaca.svg?branch=master)](https://travis-ci.org/alpaca-lang/alpaca)\n\nAlpaca is a statically typed, strict/eagerly evaluated, functional programming language for the Erlang virtual machine (BEAM).  At present it relies on type inference but does provide a way to add type specifications to top-level function and value bindings.  It was formerly known as ML-flavoured Erlang (MLFE).\n\n# TLDR; How Do I Use It?\nMake sure the following are installed:\n\n- Erlang OTP 19.3 or above ([packages from Erlang Solutions](https://www.erlang-solutions.com/resources/download.html), most development at present uses OTP 19.3 and 20.0 locally from [kerl](https://github.com/kerl/kerl))\n- [Rebar3](https://rebar3.org)\n- a build of Alpaca itself\n\n## Installing Alpaca\nReleases for OTP 19.3 and 20.0 are built by Travis CI and are [available under this repository's releases page here](https://github.com/alpaca-lang/alpaca/releases).  You will want one of the following:\n\n- `alpaca_19.3.tgz`\n- `alpaca_20.0.tgz`\n\nYou can unpack these anywhere and point the environment variable `ALPACA_ROOT` at the base folder, or place the `beams` sub-folder in any of the following locations:\n\n- `/usr/lib/alpaca`\n- `/usr/local/lib/alpaca`\n- `/opt/alpaca`\n\nPlease see the [rebar3 plugin documentation](https://github.com/alpaca-lang/rebar_prv_alpaca) for more details.\n\n## Using Alpaca in a Project\nMake a new project with `rebar3 new app your_app_name` and in the\n`rebar.config` file in your project's root folder\n(e.g. `your_app_name/rebar.config`) add the following:\n\n```erlang\n{plugins, [\n    {rebar_prv_alpaca, \".*\", {git, \"https://github.com/alpaca-lang/rebar_prv_alpaca.git\", {branch, \"master\"}}}\n]}.\n\n{provider_hooks, [{post, [{compile, {alpaca, compile}}]}]}.\n```\n\nCheck out\n[the tour for the language basics](https://github.com/alpaca-lang/alpaca/blob/master/Tour.md),\nput source files ending in `.alp` in your source folders, run `rebar3\ncompile` and/or `rebar3 eunit`.\n\n# Building and Using Your Own Alpaca\nRather than using an official build, you can build and test your own version of Alpaca.  Please note that Alpaca now needs itself in order to build.  The basic steps are:\n\n- Clone and/or modify Alpaca to suit your needs.\n- Compile your build with `rebar3 compile`.\n- Make a local untagged release for your use with `bash ./make-release.sh` in the root folder of Alpaca.\n\nThen export `ALPACA_ROOT`, e.g. in the Alpaca folder:\n\n    export ALPACA_ROOT=`pwd`/alpaca-unversioned_`\n\nThe\trebar3 plugin should now find the Alpaca binaries you built above.\n\n## Editor Support\n\nAlpaca plugins are available for various editors.\n\n- Emacs: [alpaca-mode](https://github.com/alpaca-lang/alpaca-mode)\n- Vim: [alpaca_vim](https://github.com/lepoetemaudit/alpaca_vim)\n- Visual Studio Code: [alpaca-vscode](https://github.com/alpaca-lang/alpaca-vscode)\n\n# Intentions/Goals\nSomething that looks and operates a little bit like an ML on the Erlang VM with:\n\n- Static typing of itself.  We're deliberately ignoring typing of Erlang\n  code that calls into Alpaca.\n- Parametric polymorphism\n- Infinitely recursive functions as a distinct and allowable type for processes\nlooping on receive.\n- Recursive data types\n- Syntax somewhere between [OCaml](https://ocaml.org/) and\n[Elm](http://elm-lang.org/)\n- FFI to Erlang code that does not allow the return of values typed as `term()` or `any()`\n- Simple test annotations for something like\n  [eunit](http://erlang.org/doc/apps/eunit/chapter.html), tests live beside the\n  functions they test\n\nThe above is still a very rough and incomplete set of wishes.  In future it\nmight be nice to have dialyzer check the type coming back from the FFI and\nsuggest possible union types if there isn't an appropriate one in scope.\n\n## What Works Already\n\n- Type inferencer with ADTs.  Tuples, maps, and records for product types and\n  unions for sum.  Please note that Alpaca's records are not compatible with Erlang records as the former are currently compiled to maps.\n- Compile type-checked source to `.beam` binaries\n- Simple FFI to Erlang\n- Type-safe message flows for processes defined inside Alpaca\n\nHere's an example module:\n\n```elm\nmodule simple_example\n\n-- a basic top-level function:\nlet add2 x = x + 2\n\nlet something_with_let_bindings x =\n  -- a function:\n  let adder a b = a + b in\n  -- a variable (immutable):\n  let x_plus_2 = adder x 2 in\n  add2 x\n\n-- a polymorphic ADT:\ntype messages 'x = 'x | Fetch pid 'x\n\n{- A function that can be spawned to receive `messages int`\n    messages, that increments its state by received integers\n    and can be queried for its state.\n-}\nlet will_be_a_process x = receive with\n    i -\u003e will_be_a_process (x + i)\n  | Fetch sender -\u003e\n    let sent = send x sender in\n    will_be_a_process x\n\nlet start_a_process init = spawn will_be_a_process init\n```\n\n# Licensing\nAlpaca is released under the terms of the Apache License, Version 2.0\n\nCopyright 2016 Jeremy Pierre\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n\n# Contributions and Help\nPlease note that this project is released with a Contributor Code of\nConduct, version 1.4. By participating in this project you agree to abide by its\nterms.  See `code_of_conduct.md` for details.\n\nYou can join `#alpaca-lang` on [freenode](http://freenode.net) to discuss the\nlanguage (directions, improvement) or get help.  This IRC channel is\ngoverned by the same code of conduct detailed in this repository.\n\nPull requests with improvements and bug reports with accompanying\ntests welcome.\n\n# Using It\nIt's still quite early in Alpaca's evolution but the tests should give a relatively clear picture as to where we're going.  `test_files` contains some example source files used in unit tests.  You can call\n`alpaca:compile({files, [List, Of, File, Names, As, Strings]}, [list, of, options])` or `alpaca:compile({text, CodeAsAString}, [options, again])` for now but generally we recommend using the rebar3 plugin.\n\nSupported options are:\n* `'test'` - This option will cause all tests in a module to be type checked and exported\n    as functions that  [EUnit](http://erlang.org/doc/apps/eunit/chapter.html) should pick up.\n* `{'warn_exhaustiveness', boolean()}` - If set to true (the default), the compiler will print warnings regarding missed patterns in top level functions.\n\nErrors from the compiler (e.g. type errors)\nare almost comically hostile to usability at the moment.  See the\ntests in `alpaca_typer.erl`.\n\n## Prerequisites\nYou will generally want the following two things installed:\n\n- Erlang/OTP 19.3 or above ([packages from Erlang Solutions](https://www.erlang-solutions.com/resources/download.html), most development so far uses OTP 19.3 and 20.0 locally from [kerl](https://github.com/kerl/kerl))\n- [Rebar3](https://rebar3.org)\n\n## Writing Alpaca with Rebar3\nThanks to [@tsloughter](https://github.com/tsloughter)'s\n[Alpaca Rebar3 plugin](https://github.com/alpaca-lang/rebar_prv_alpaca)\nit's pretty easy to get up and running.\n\nMake a new project with Rebar3 (substituting whatever project name\nyou'd like for `alpaca_example`):\n\n    $ rebar3 new app alpaca_example\n    $ cd alpaca_example\n\nIn the `rebar.config` file in your project's root folder add the\nfollowing (borrowed from @tsloughter's docs):\n\n```erlang\n{plugins, [\n    {rebar_prv_alpaca, \".*\", {git, \"https://github.com/alpaca-lang/rebar_prv_alpaca.git\", {branch, \"master\"}}}\n]}.\n\n{provider_hooks, [{post, [{compile, {alpaca, compile}}]}]}.\n```\n\nNow any files in the project's source folders that end with the\nextension `.alp` will be compiled and included in Rebar3's output\nfolders (provided they type-check and compile successfully of course).\nFor a simple module, open `src/example.alp` and add the following:\n\n```elm\nmodule example\n\nexport add/2\n\nlet add x y = x + y\n```\n\nThe above is just what it looks like:  a module named `example` with a\nfunction that adds two integers.  You can call the function directly\nfrom the Erlang shell after compiling like this (note alpaca prepends `alpaca_` to the module name, so in the erlang shell you must explicitly add this):\n\n    $ rebar3 shell\n    ... compiler output skipped ...\n    1\u003e alpaca_example:add(2, 6).\n    8\n    2\u003e\n\nNote that calling Alpaca from Erlang won't do any type checking but if\nyou've written a variety of Alpaca modules in your project, all their\ninteractions with each other will be type checked and safe (provided\nthe compile succeeds).\n\n## Compiler Hacking\nIf you have installed the prerequisites given above, clone this\nrepository and run tests and dialyzer with:\n\n    rebar3 eunit\n    rebar3 dialyzer\n\nThere's no command line front-end for the compiler so unless you use\n@tsloughter's Rebar3 plugin detailed in the previous section, you will\nneed to boot the erlang shell and then run `alpaca:compile/2` to build\nand type-check things written in Alpaca.  For example, if you wanted to\ncompile the type import test file in the `test_files` folder:\n\n    rebar3 shell\n    ...\n    1\u003e Files = [\"test_files/basic_adt.alp\", \"test_files/type_import.alp\"].\n    2\u003e alpaca:compile({files, Files}, []).\n\nThis will result in either an error or a list of tuples of the following form:\n\n    {compiled_module, ModuleName, FileName, BeamBinary}\n\nThe files will not actually be written by the compiler so the binaries\ndescribed by the tuples can either be loaded directly into the running\nVM (see the tests in `alpaca.erl`) or written manually for now unless of\ncourse you're using the aforementioned rebar3 plugin/\n\n## Built-In Stuff\nMost of the basic Erlang data types are supported:\n\n- booleans, `true` or `false`\n- atoms, `:atom`, `:\"Quoted Atom!\"`\n- floats, `1.0`\n- integers, `1`\n- strings, `\"A string\"`.  These are encoded as UTF-8 binaries.\n- character lists, like default Erlang strings, `c\"characters here\"`\n- lists, `[1, 2, 3]` or `1 :: 2 :: [3]`\n- binaries, `\u003c\u003c\"안녕, this is some UTF-8 text\": type=utf8\u003e\u003e`, `\u003c\u003c1, 2,\n  32798: type=int, size=16, signed=false\u003e\u003e`, etc\n- tuples, `(\"a\", :tuple, \"of arity\", 4)`\n- maps (basic support), e.g. `#{:atom_key =\u003e \"string value\"}`.  These\nare statically typed as lists are (generics, parametric polymorphism).\n- records (basic support), these look a bit like OCaml and Elm records, e.g. `{x=1, hello=\"world\"}` will produce a record with an `x: int` and `hello: string` field.  Please see the  [language tour](https://github.com/alpaca-lang/alpaca/blob/master/Tour.md) for more details.\n- pids, these are also parametric (like lists, \"generics\").  If you're\n  including them in a type you can do something like `type t = int |\n  pid int` for a type that covers integers and processes that receive integers.\n\nIn addition there is a unit type, expressed as `()`.\n\nNote that the tuple example above is typed as a tuple of arity 4 that\nrequires its members to have the types `string`, `atom`, `string`,\n`integer` in that order.\n\nOn top of that you can define ADTs, e.g.\n\n```elm\ntype try 'success 'error = Ok 'success | Error 'error\n```\n\nAnd ADTs with more basic types in unions work, e.g.\n\n```elm\ntype json = int | float | string | bool\n          | list json\n          | list (string, json)\n```\n\nTypes start lower-case, type constructors upper-case.\n\nInteger and float math use different symbols as in OCaml, e.g.\n\n```elm\n1 + 2      -- ok\n1.0 + 2    -- type error\n1.0 + 2.0  -- type error\n1.0 +. 2.0 -- ok\n```\n\nBasic comparison functions are in place and are type checked, e.g. `\u003e`\nand `\u003c` will work both in a guard and as a function but:\n\n```elm\n1 \u003e 2             -- ok\n1 \u003c 2.0           -- type error\n\"Hello\" \u003e \"world\" -- ok\n\"a\" \u003e 1           -- type error\n```\n\nSee `src/builtin_types.hrl` for the included functions.\n\n## Pattern Matching\nPretty simple and straightforward for now:\n\n```elm\nlet length l = match l with\n    [] -\u003e 0\n  | h :: t -\u003e 1 + (length t)\n  ```\n\nThe first clause doesn't start with `|` since it's treated like a\nlogical OR.\n\nPattern match guards in clauses essentially assert types, e.g. this\nwill evaluate to a `t_bool` type:\n\n```elm\nmatch x with\n  b, is_bool b -\u003e b\n```\n\nand\n\n```elm\nmatch x with\n  (i, f), is_integer i, is_float f -\u003e :some_tuple\n```\n\nwill type to a tuple of `integer`, `float`.\n\nSince strings are currently compiled as UTF-8 Erlang binaries, only the first clause will ever match:\n\n```elm\ntype my_binary_string_union = binary | string\n\nmatch \"Hello, world\" with\n    b, is_binary b -\u003e b\n  | s, is_string s -\u003e s\n```\n\nFurther, nullary type constructors are encoded as atoms and unary\nconstructors in tuples led by atoms, e.g.\n\n```elm\ntype my_list 'x = Nil | Cons ('x, my_list 'x)\n```\n\n`Nil` will become `'Nil'` after compilation and `Cons (1, Nil)` will\nbecome `{'Cons', {1, 'Nil'}}`.  Exercise caution with the order of\nyour pattern match clauses accordingly.\n\n### Maps\nNo distinction is made syntactically between map literals and map\npatterns (`=\u003e` vs `:=` in Erlang), e.g\n\n```elm\nmatch my_map with\n  #{:a_key =\u003e some_val} -\u003e some_val\n```\n\nYou can of course use variables to match into a map so you could write\na simple get-by-key function as follows:\n\n```elm\ntype my_opt 'a = Some 'a | None\n\nlet get_by_key m k =\n  match m with\n      #{k =\u003e v} -\u003e Some v\n    | _ -\u003e None\n```\n\n\n## Modules (The Erlang Kind)\nML-style modules aren't implemented at present.  For now modules in Alpaca are the same as\nmodules in Erlang with top-level entities including:\n\n- a module name (required)\n- function exports (with arity, as in Erlang)\n- type imports (e.g. `use module.type`)\n- type declarations (ADTs)\n- functions which can contain other functions and variables via `let`\nbindings.\n- functions are automatically curried (with some limitations)\n- simple test definitions\n\nAn example:\n\n```elm\nmodule try\n\nexport map/2  -- separate multiple exports with commas\n\n-- type variables start with a single quote:\ntype maybe_success 'error 'ok = Error 'error | Success 'ok\n\n-- Apply a function to a successful result or preserve an error.\nlet try_map e f = match e with\n    Error _ -\u003e e\n  | Success ok -\u003e Success (f ok)\n```\n\n\n### Tests\nTests are expressed in an extremely bare-bones manner right now and\nthere aren't even proper assertions available.  If the compiler is\ninvoked with options `[test]`, the following will synthesize and\nexport a function called `add_2_and_2_test`:\n\n```elm\nlet add x y = x + y\n\ntest \"add 2 and 2\" =\n  let res = add 2 2 in\n  assert_equal res 4\n\nlet assert_equal x y =\n  match x == y with\n    | true -\u003e :ok\n    | _ -\u003e throw (:not_equal, x, y)\n```\n\n\nAny test that throws an exception will fail so the above would work\nbut if we replaced `add/2` with `add x y = x + (y + 1)` we'd get a\nfailing test.  If you use the rebar3 plugin mentioned above, `rebar3\neunit` should run the tests you've written.  There's a bug currently\nwhere the very first test run _won't_ execute the tests but all runs\nafter will (not sure why yet).\n\nThe expression that makes up a test's body is type inferenced and\nchecked.  Type errors in a test will always cause a compilation error.\n\n## Processes\nAn example:\n\n```elm\nlet f x = receive with\n  (y, sender) -\u003e\n    let z = x + y in\n    let sent = send z sender in\n  f z\n\nlet start_f init = spawn f init\n```\n\n\nAll of the above is type checked, including the spawn and message sends.\nAny expression that contains a `receive` block becomes a \"receiver\"\nwith an associated type.  The type inferred for `f` above is the\nfollowing:\n\n```erlang\n{t_receiver,\n  {t_tuple, [t_int, {t_pid, t_int}]},\n  {t_arrow, [t_int], t_rec}}\n```\n\n\nThis means that:\n\n- `f` has it's own function type (the `t_arrow` part) but it _also_\n  contains one or more receive calls that handle tuples of integers\n  and PIDs that receive integers themselves.\n- `f`'s function type is one that takes integers and is infinitely\nrecursive.\n\n`send` returns `unit` but there's no \"do\" notation/side effect support\nat the moment hence the let binding.  `spawn` for the moment can only\nstart functions defined in the module it's called within to simplify\nsome cross-module lookup stuff for the time being.  I intend to\nsupport spawning functions in other modules fairly soon.\n\nNote that the following will yield a type error:\n\n```elm\nlet a x = receive with\n  i -\u003e b x + i\n\nlet b x = receive with\n  f -\u003e a x +. i\n```\n\n\nThis is because `b` is a `t_float` receiver while `a` is a `t_int`\nreceiver.  Adding a union type like `type t = int | float` will solve\nthe type error.\n\nIf you spawn a function which nowhere in its call graph posesses a\n`receive` block, the pid will be typed as `undefined`, which means\n_all_ message sends to that process will be a type error.\n\n## Current FFI\nThe FFI is quite limited at present and operates as follows:\n\n```elm\nbeam :a_module :a_function [3, \"different\", \"arguments\"] with\n    (ok, _) -\u003e :ok\n  | (error, _) -\u003e :error\n```\n\n\nThere's clearly room to provide a version that skips the pattern match and\nsucceeds if dialyzer supplies a return type for the function that matches a type\nin scope (union or otherwise).  Worth noting that the FFI assumes you\nknow what you're doing and does _not_ check that the module and\nfunction you're calling exist.\n\n# Localization\nCompiler error messages may be localized by calling `alpaca_error_format:fmt/2`.\nIf no translation is available in the specified locale, the translation for\nen_US will be used.\n\nLocalization is performed using gettext \".po\" files stored in priv/lang. To add\na new language, say Swedish (sv_SE), create a new file priv/lang/alpaca.sv_SE.po.\nIf you use Poedit, you may then import all messages to be translated by selecting\n\"Catalog -\u003e Update from POT file...\" in the menu, and then pick priv/lang/alpaca.pot.\nThe messages may be a bit cryptic. Use the en_US as an aid to understand them.\n\nThe POT file is automatically updated whenever alpaca is compiled. Updates to\npo-files are also picked up at the compile phase.\n\n# Problems\n## What's Missing\nA very incomplete list:\n\n- `self()` - it's a little tricky to type.  The type-safe solution is\n  to spawn a process and then send it its own pid.  Still thinking\n  about how to do this better.\n- exception handling (try/catch)\n- any sort of standard library.  Biggest missing things right\n  now are things like basic string manipulation functions and\n  adapters for `gen_server`, etc.\n- anything like behaviours or things that would support them.  Traits,\ntype classes, ML modules, etc all smell like supersets but we don't have a\ndefinite direction yet.\n- simpler FFI, there's an open issue for discussion:  https://github.com/alpaca-lang/alpaca/issues/7\n- annotations in the BEAM file output (source line numbers, etc).  Not\n  hard based on what can be seen in the [LFE](https://github.com/lfe)\n  code base.\n- support for typing anything other than a raw source file.\n- side effects, like using `;` in OCaml for printing in a function\n  with a non-unit result.\n\n## Implementation Issues\nThis has been a process of learning-while-doing so there are a number of issues with\nthe code, including but not limited to:\n\n- there's a lot of cruft around error handling that should all be\n  refactored into some sort of basic monad-like thing.  This is\n  extremely evident in `alpaca_ast_gen.erl` and `alpaca_typer.erl`.  Frankly\n  the latter is begging for a complete rewrite.\n- type unification error line numbers can be confusing.  Because of\n  the sequence of unification steps, sometimes the unification error\n  might occur at a function variable's location or in a match\n  expression rather than in the clauses.  I'm considering tracking the\n  history of changes over the course of unifications in a reference\n  cell in order to provide a typing trace to the user.\n- generalization of type variables is incompletely applied.\n\n# Parsing Approach\nParsing/validating occurs in several passes:\n\n1. `yecc` for the initial rough syntax form and basic module structure.  This is\n   where exports and top-level function definitions are collected and the\n   initial construction of the AST is completed.\n2. Validating function definitions and bindings inside of them.  This stage uses\n   environments to track whether a function application is referring to a known\n   function or a variable.  The output of this stage is either a module\n   definition or a list of errors found.  This stage also renames\n   variables internally.\n3. Type checking.  This has some awkward overlaps with the environments built in\n   the previous step and may benefit from some interleaving at some point.  An\n   argument against this mixing might be that having all functions defined\n   before type checking does permit forward references.\n\n## AST Construction\nSeveral passes internally\n\n- for each source file (module), validate function definitions and report syntax\n  errors, e.g. params that are neither unit nor variable bindings (so-called\n  \"symbols\" from the `yecc` parser), building a list of top-level internal-only\n  and exported functions for each module.  The output of this is a global\n  environment containing all exported functions by module and an environment of\n  top-level functions per module _or_ a list of found errors.\n- for each function defined in each module, check that every variable and\n  function reference is valid.  For function applications, arity is checked\n  where the function applied is not in a variable.\n\n# Type Inferencing and Checking\nAt present this is based off of the sound and eager type inferencer in\nhttp://okmij.org/ftp/ML/generalization.html with some influence from\nhttps://github.com/tomprimozic/type-systems/blob/master/algorithm_w where the\narrow type and type schema instantiation are concerned.\n\n## Single Module Typing\n\n```elm\nmodule example\n\nexport add/2\n\nlet add x y = adder x y\n\nlet adder x y = x + y\n```\n\nThe forward reference in `add/2` is permitted but currently leads to some wasted\nwork.  When typing `add/2` the typer encounters a reference to `adder/2` that is\nnot yet bound in its environment but is available in the module's definition.\nThe typer will look ahead in the module's definition to determine the type of\n`adder/2`, use it to type `add/2`, and then throw that work away before\nproceeding to type `adder/2` again.  It may be beneficial to leverage something\nlike [ETS](http://erlang.org/doc/man/ets.html) here in the near term.\n\n## Recursion\nInfinitely recursive functions are typed as such and permitted as they're\nnecessary for processes that loop on `receive`.  Bi-directional calls between modules\nare disallowed for simplicity.  This means that given module `A` and `B`, calls\ncan occur from functions in `A` to those in `B` or the opposite but *not* in\nboth directions.\n\nI think this is generally pretty reasonable as bidirectional references probably\nindicate a failure to separate concerns but it has the additional benefit of\nbounding how complicated inferencing a set of mutually recursive functions can\nget.  The case I'm particularly concerned with can be illustrated with the\nfollowing `Module.function` examples:\n\n```elm\nlet A.x = B.y ()\nlet B.y = C.z ()\nlet C.z = A.x ()\n```\n\nThis loop, while I belive possible to check, necessitates either a great deal of\nstate tracking complexity or an enormous amount of wasted work and likely has\nsome nasty corner cases I'm as yet unaware of.\n\nThe mechanism for preventing this is simple and relatively naive to start:\nentering a module during type inferencing/checking adds that module to the list\nof modules encountered in this pass.  When a call occurs (a function application\nthat crosses module boundaries), we check to see if the referenced module is\nalready in the list of entered modules.  If so, type checking fails with an\nerror.\n\n## No \"Any\" Type\nThere is currently no \"any\" root/bottom type.  This is going to be a problem for\nsomething like a simple `println`/`printf` function as a simple to use version\nof this would best take a List of Any.  The FFI to Erlang code gets around this\nby not type checking the arguments passed to it and only checking the result\nportion of the pattern matches.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpaca-lang%2Falpaca","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falpaca-lang%2Falpaca","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falpaca-lang%2Falpaca/lists"}