{"id":20132469,"url":"https://github.com/ollef/sixten","last_synced_at":"2025-04-04T16:13:50.128Z","repository":{"id":66322185,"uuid":"107585146","full_name":"ollef/sixten","owner":"ollef","description":"Functional programming with fewer indirections","archived":false,"fork":false,"pushed_at":"2020-08-20T12:57:06.000Z","size":2024,"stargazers_count":760,"open_issues_count":38,"forks_count":26,"subscribers_count":48,"default_branch":"master","last_synced_at":"2025-03-28T15:06:42.053Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ollef.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-10-19T18:44:48.000Z","updated_at":"2025-03-19T07:51:34.000Z","dependencies_parsed_at":null,"dependency_job_id":"b1a5037a-6af2-4d2f-98ee-25b341b00259","html_url":"https://github.com/ollef/sixten","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollef%2Fsixten","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollef%2Fsixten/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollef%2Fsixten/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ollef%2Fsixten/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ollef","download_url":"https://codeload.github.com/ollef/sixten/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247208141,"owners_count":20901570,"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":[],"created_at":"2024-11-13T20:53:43.377Z","updated_at":"2025-04-04T16:13:50.104Z","avatar_url":"https://github.com/ollef.png","language":"Haskell","readme":"# Sixten [![Build Status](https://travis-ci.org/ollef/sixten.svg?branch=master)](https://travis-ci.org/ollef/sixten) [![Gitter chat](https://badges.gitter.im/ollef/sixten.svg)](https://gitter.im/sixten-lang)\n\n```\n     o\n ,---.-. ,-|- ,--.--.\n `---|  |  |  |--'  |\n `---'-' `-'--`--'  `-'\n```\n\nSixten is an experimental functional programming language where all data is\nunboxed by default. Functional programming with fewer indirections!\n\nNOTE: Sixten is not dead! A new frontend is currently being built over at [Sixty](https://github.com/ollef/sixty), which will be merged into Sixten eventually.\n\nBelow follow some of Sixten's features that work now.\n\n### Unboxed stack- or heap-allocated data types\n\nWe can define a new type of tuples of two given types as:\n\n```haskell\ntype Pair a b = MkPair a b\n```\n\nWe can then use the constructor `MkPair` to construct a pair. As an example,\n`MkPair 610 \"Sixten\"` has type `Pair Int String`.\n\nThe size in memory of `Pair a b` is the size of `a` plus the size of `b`, and\nthe pair is passed in registers or on the stack when used in a function.\n\nOn a 64-bit machine, `sizeOf Int = 8` and `sizeOf (Pair Int Int) = 16`.\n\n_Nested_ pairs are also laid out flat in memory: `sizeOf (Pair Int (Pair Int Int)) = 24`.\n\nWith this in mind we can define stack-allocated, flat, unboxed vectors of a\ngiven number of elements as:\n\n```haskell\nVector : Nat -\u003e Type -\u003e Type\nVector Zero _ = Unit\nVector (Succ n) a = Pair a (Vector n a)\n```\n\nHere, `Unit` is the zero-size type with one inhabitant, and `Pair A B`\nthe type of unboxed pairs of `A` and `B`.\n\nHere's a function that sums a vector of ints:\n\n```haskell\nsum : (n : Nat) -\u003e Vector n Int -\u003e Int\nsum Zero MkUnit = 0\nsum (Succ n) (MkPair x xs) = addInt x (sum n xs)\n```\n\nThe type of heap-allocated vectors is then `Ptr (Vector n a)`, where `Ptr`\nis a built-in type of type `Type -\u003e Type` which returns a boxed version of\nthe argument. For any type `t`, `sizeOf (Ptr t) = 8` on a 64-bit machine.\nUsing `Ptr` we can define (immutable) arrays, where the length is not visible\nin the type, as:\n\n```haskell\ntype Array a where\n  MkArray : (n : Nat) -\u003e Ptr (Vector n a) -\u003e Array a\n```\n\nAnother example where `Ptr` can be used is to define a type of linked lists:\n\n```haskell\ntype List a = Nil | Cons a (Ptr (List a))\n```\n\nHere `Ptr` makes it explicit that a `Cons` cell has a _pointer to_ a list.\nNote that the `a` is unpacked in `Cons` (unless it's a `Ptr something`),\nso it's an _intrusive_ linked list where the tail pointer is next to the\n`a` element in memory.\n\n### Algebraic data types and pattern matching\n\nThere are two ways to define algebraic data types:\n\n- ADT-style:\n\n```haskell\ntype Maybe a = Nothing | Just a\n```\n\n- GADT-style:\n\n```haskell\ntype Maybe a where\n  Nothing : Maybe a\n  Just : a -\u003e Maybe a\n```\n\nPattern matching can be done in clauses and case expressions:\n\n```haskell\nfromMaybe : forall a. a -\u003e Maybe a -\u003e a\nfromMaybe def Nothing = def\nfromMaybe _ (Just x) = x\n```\n\n```haskell\nfromMaybe' : forall a. a -\u003e Maybe a -\u003e a\nfromMaybe' def mx = case mx of\n  Nothing -\u003e def\n  Just x -\u003e x\n```\n\nIn memory, algebraic data types are represented by an integer tag next to a\nchunk of memory big enough to hold the contents of any of the constructors.\nIf the type has fewer than two constructors it's represented without a tag.\n\nThis means that `type Unit = MkUnit` has size zero, and `Maybe A` has size\n`tagSize + sizeOf A`.\n\n### Implicit parameters\n\nThe `forall` keyword introduces implicit parameters in a type:\n\n```haskell\nid : forall a. a -\u003e a\nid x = x\n```\n\nImplicit arguments are inferred by usage, so `a = Int` in `id 610`.  They\ncan also be explicitly specified using `@`: `id @Int 610`.\n\n### Dependent function types (pi types)\n\nThe arguments in function types can be named and used in the return type:\n\n```haskell\nsum : (n : Nat) -\u003e Vector n Int -\u003e Int\n```\n\n### Inductive families (GADTs)\n\nConstructors can constrain their type's parameters. For instance, we can define\npropositional equality as follows:\n\n```haskell\ntype Equals a b where\n  Refl : Equals a a\n```\n\nHere, the `Refl` constructor can only be used when the two parameters to\n`Equals` are equal. When given a value of type `Equals a b`, we can use pattern\nmatching to reveal the constraint that `a` and `b` are equal. As an example,\nhere's how to define transitivity:\n\n```haskell\ntrans : forall a b c. Equals a b -\u003e Equals b c -\u003e Equals a c\ntrans Refl Refl = Refl\n```\n\n### Type inference\n\nSixten's type inference is inspired by\n[Practical type inference for arbitrary-rank types](https://www.microsoft.com/en-us/research/publication/practical-type-inference-for-arbitrary-rank-types/).\n\n### Classes\n\nThese are similar to Haskell's type classes.\n\nAs an example, here's how to define a `Functor` class:\n\n```haskell\nclass Functor f where\n  map : forall a b. (a -\u003e b) -\u003e f a -\u003e f b\n\ntype Maybe a = Nothing | Just a\n\ninstance Functor Maybe where\n  map _ Nothing = Nothing\n  map f (Just x) = Just (f x)\n```\n\n### Extern C code/FFI\n\nExtern C code can be written in `(C| ... |)` blocks. Sixten expressions can be\nspliced into the code with `$expr`. For example, here's how addition on the\nbuilt-in type `Int` can be defined:\n\n```haskell\naddInt : Int -\u003e Int -\u003e Int\naddInt x y = (C|\n  return $x + $y;\n|)\n```\n\nExtern C code blocks are compiled to C functions which are compiled separately.\nLLVM link-time optimisation is used to minimise function-call overheads.\n\n### Compilation to LLVM\n\nThis currently uses the Boehm–Demers–Weiser garbage collector for\ngarbage-collecting heap-allocated data.\n\n### Boxed types\n\nSometimes we do want boxed types, e.g. to define linked lists without having to\nexplicitly wrap and unwrap values in `Ptr`s. This can be done with the `boxed`\nkeyword:\n\n```haskell\nboxed\ntype List a = Nil | Cons a (List a)\n```\n\nThis means that all values of type `List t` are indirected through a pointer,\nas if using `Ptr (List t)`. Boxed types can sometimes be represented more efficiently than pointers to unboxed types, because\ntheir size does not need to be padded to be uniform with the biggest constructor.\n\nThe `Ptr` type from the `Builtin` module is defined using `boxed`:\n\n```haskell\nboxed\ntype Ptr a = Ref a\n```\n\n## Planned features\n\n* Records\n* Effects and IO\n* Standard library\n* Infix and/or mixfix definitions\n* Dedicated garbage collector\n\nSee the issues list for more details about what's planned.\n\n## Compared to other languages\n\nSixten is very related to other functional languages such as Haskell, Agda, and\nIdris. The biggest difference between other languages and Sixten is the way\nthat Sixten allows us to control the memory layout of data.\n\nMost high-level languages with parametrically polymorphic (or generic) data\ntypes and functions, even if it is offered under a different name like\ntemplates, fall into one of the following two categories:\n\n1.  They use a uniform representation for polymorphic data, which is usually\n    word-sized. If the data is bigger than a word it's represented as a pointer\n    to the data.\n\n2.  They use monomorphisation or template instantiation, meaning that new code\n    is generated statically whenever a polymorphic function is used at a new\n    type.\n\nNeither of these approaches is perfect: With the uniform representation\napproach we lose control over how our data is laid out in memory, and with the\ntemplate instantiation approach we lose modularity and polymorphic recursion:\n\nWith a uniform representation we cannot for example define polymorphic\nintrusive linked lists, where the node data is stored next to the list's\n\"next pointer\". Given the (Haskell) list definition\n\n```haskell\ndata List a = Nil | Cons a (List a)\n```\n\nThe representation in memory of the list `Cons x (Cons y Nil)` in languages\nwith a uniform representation is something like:\n\n```\n     [x]           [y]\n      ^             ^\n      |             |\n[Cons * *]---\u003e[Cons * *]---\u003e[Nil]\n```\n\nWe cannot define a polymorphic list whose representation is intrusive:\n\n```\n[Cons x *]---\u003e[Cons y *]---\u003e[Nil]\n```\n\nWhat we gain from using a uniform representation is modularity: A\npolymorphic function, say `map : forall a b. (a -\u003e b) -\u003e List a -\u003e List b`, can be\ncompiled once and used for any types `a` and `b`.\n\nWith monomorphisation, we are able to define intrusive lists, like in the\nfollowing C++-like code:\n\n```c++\ntemplate\u003ctypename A\u003e\nclass List\n{\n  A element;\n  List\u003cA\u003e* next;\n}\n```\n\nHowever, unless we know all the types that `A` will be instantiated with in\nadvance, we have to generate new code for every instantiation of the\nfunction, meaning that we have partly lost modular compilation. We also\ncan't have polymorphic recursion since that would require an unbounded\nnumber of instantiations. Template instantiation also leads to bigger code\nsince it generates multiple versions of the same function.\n\nWhat is gained is the ability to more finely express how our data is laid\nout in memory, which for instance means that we can write code that is\ncache-aware and which uses fewer memory allocations.\n\nSixten gives us both: it allows us to control the memory layout of our data\nall the while retaining modularity.\n\nThe definition of the list type in Sixten is\n\n```haskell\ntype List a = Nil | Cons a (Ptr (List a))\n```\n\nThe difference between Sixten and (for instance) Haskell is that everything is\nunboxed by default, meaning that the `a` field in the `Cons` constructor is not\nrepresented by a pointer to an `a`, but it _is_ an `a`. This also means that we\nhave to mark where we actually want pointers with the `Ptr` type constructor.\nThe `Cons` constructor has to hold a _pointer to_ the tail of the list because\nwe would otherwise create an infinite-size datatype, which is not allowed in\nSixten.\n\nThe novel feature that allows this is _type representation polymorphism_.\nTypes are compiled to their representation in Sixten. In the current\nimplementation of Sixten the representation consists only of the type's size in\nmemory, so e.g. `Int` is compiled to the integer `8` on a 64-bit system.  A\npolymorphic function like `map : forall a b. (a -\u003e b) -\u003e List a -\u003e List b`\nimplicitly takes the types `a` and `b` as arguments at runtime, and its\ncompiled form makes use of the type representation information to calculate\nthe memory offsets and sizes of its arguments and results that are needed to\nbe representation polymorphic.\n\nThis kind of polymorphism is potentially slower than specialised functions\nsince it passes around additional implicit arguments and does more calculation\nat runtime. Some of this inefficiency should be offset by having better memory\nlayout than systems using uniform representations, meaning better cache\nbehaviour. Also note that type representation polymorphism does not preclude\ncreating specialised versions of functions known to be performance-critical,\nmeaning that we can choose to use monomorphisation when we want to.\n\nSixten's type representation polymorphism is closely related to research on\n_intensional polymorphism_. What sets Sixten apart is the way type\nrepresentations are used in the compiled code. Sixten doesn't need to use type\nrepresentations to perform code selection, but rather compiles polymorphic\nfunctions to _single_ implementations that leverage the information in the\ntype representation to be general enough to work for all types.\nType representations are also not structural in Sixten, but consist simply\nof the size of the type.\n\n## Installation\n\nTo build Sixten from source, clone this repository and build it using\n[Stack](https://www.haskellstack.org):\n\n     git clone git@github.com:ollef/sixten.git\n     cd sixten\n     stack build\n\nTo install the `sixten` binary, run:\n\n     stack install\n\nThis will install `sixten` locally, which usually means `~/.local/bin`. This\npath can be added to your `PATH` environment variable if desired.\n\nTo run tests, run:\n\n     stack test\n\n### Sixten compilation dependencies\n\nTo build Sixten programs, you'll need:\n\n* LLVM and Clang \u003e= 6. The `--llvm-config` flag can be used to tell `sixten` where\n  to look for these.\n* The Boehm–Demers–Weiser garbage collector library.\n* pkg-config (used to find the Boehm–Demers–Weiser GC library).\n\n### Editor integration\n\nThe compiler has a work-in-progress language server following the [Language Server Protocol](https://langserver.org/).\n\nTo install it, set up your editor's language client to use the command\n`sixten language-server`.\n\nThe language server currently has the following limitations:\n\n* Only diagnostic reporting and hovering is supported.\n* Hovering only works in certain contexts.\n* Only single file projects are supported.\n* Everything is recompiled on each save.\n\n#### Vim\n\nHere's how to set up Sixten with\n[LanguageClient-neovim](https://github.com/autozimu/LanguageClient-neovim/)\nusing\n[vim-plug](https://github.com/junegunn/vim-plug):\n\n```viml\n\" Language client plugin\nPlug 'autozimu/LanguageClient-neovim', {\n  \\ 'branch': 'next',\n  \\ 'do': './install.sh'\n  \\ }\n\n\" Syntax highlighting and filetype detection\nPlug 'ollef/sixten', { 'rtp': 'vim' }\n\n\" Use the Sixten language server for vix files\nlet g:LanguageClient_serverCommands = {\n  \\ 'sixten': ['~/.local/bin/sixten', 'language-server']\n  \\ }\n```\n\nThe above assumes that the `sixten` binary is installed locally in\n`~/.local/bin`.\n\n### Bash command completion\n\nWith `sixten` in your PATH, add the following to your `.bashrc` to get\ncompletion for the `sixten` command:\n\n```shell\nsource \u003c(sixten --bash-completion-script `which sixten`)\n```\n## Changelog\n\n[Here](CHANGELOG.md).\n\n## Contributions\n\nDoes this sound interesting to you? Get involved and help shape the Sixten\nlanguage! Contributions are always welcome.\n\nIf want to get in touch, create a Github issue or join the [Gitter chat](https://gitter.im/sixten-lang).\n\nPlease read the [code of conduct](CODE_OF_CONDUCT.md).\n\n## Contributors\n\n[Olle Fredriksson](https://github.com/ollef)\n\n[Victor Borja](https://github.com/vic)\n\n[Varun Gandhi](https://github.com/theindigamer)\n\n[Brandon Hamilton](https://github.com/brandonhamilton)\n\n[He Tao](https://github.com/sighingnow)\n\n[Dan Rosén](https://github.com/danr)\n","funding_links":[],"categories":["Functional"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Follef%2Fsixten","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Follef%2Fsixten","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Follef%2Fsixten/lists"}