Not only does functional programming allow for strong scalability of compact,
readable and effective code, but functional reactive programming is specifically
designed to handle timing based events in an intuitive way. Juniper is designed
to carry these advantages, as well as be lightweight enough to function viably on Arduino.
Juniper supports many features typical of functional programming languages,
including algebraic data types, tuples, records, pattern matching,
immutable data structures, parametric polymorphic functions, and anonymous functions (lambdas).
Some imperative programming concepts are also present in Juniper,
such as for, while and do while loops, the ability to mark variables
as mutable, and mutable references.
Completely Open Source
The Juniper compiler, standard library and documentation are all open
sourced under the MIT License. The Juniper compiler itself is written
in F#, an open source language originally developed by Microsoft Research.
Juniper 3.1.0 Released
December 19, 2021
Bug fixes for ensuring that C++ code compiles.
Upgrade the Juniper compiler to use .NET6.
Funarg Paper Accepted to IFL Conference
August 28, 2021
The paper that describes how Juniper uses stack allocated closures has been accepted to the 33rd Symposium on Implementation and Application of Functional Languages! Read the preprint on arXiv here: https://arxiv.org/abs/2108.07389
Juniper 3.0.0 Released
June 19, 2021
Bug fixes for the type checker.
Constraint system for safely type checking arithmetic operations and record access operations. Arithmetic operations are now polymorphic over the numeric types while retaining type safety. Record access is performed via a variation of the Simple Overloaded Record Fields" proposal.
Records are now standalone structural types, rather than being defined as standalone types. This changes the syntax of how records are created as well as how record type constraints are defined.
The alias keyword has been added to create aliases for types. This is very useful for defining aliases for record types in particular, although algebraic datatypes can be aliased as well.
A new type, rawpointer has been added. Internally rawpointer compiles to a void* C++ pointer. The null expression now creates a null pointer of type rawpointer.
The smartpointer syntax has changed. To create a smartpointer now, use the syntax smartpointer(ptr, destructor), where ptr is a rawpointer and destructor is a function that takes in a rawpointer as an argument and returns unit. Destructor is called when the number of references to the smartpointer have dropped to 0.
The internal smart pointer implementation has been re-written, fixing possible issues with the old implementation
Algebraic data types are no longer compiled to unions, and instead compile down to a custom written class similar to std::variant. This fixes the long standing issues where smartpointers and references could not be members of algebraic data types.
The syntax of algebraic data types has been changed to allow for value constructors taking multiple parameters without having to resort to tuples. The syntax is now valueConName(ty0, ty1, ..., tyn).
Inline C++ can now be placed in top level blocks. This is very useful for declaring C++ global variables.
Records can now be packed, which is very useful when interfacing over the network or over something like Bluetooth. To make a record packed, simply add the packed keyword in front of the record expression.
The capacity system now utilizes an intelligent Computer Algebra System (CAS) for solving systems of equations. Since capacities are a form of very rudementary dependent types, the CAS system can be viewed as a way of proving capacity equalities automatically.
Bug fixes for standard library functions. New standard library modules, such as the Color module have been added.
The var keyword has been added to declare variables but not initialize them. The syntax is var varName : type. Variables declared this way are mutable by default.
Function closures have been completely reworked and upgraded. The type of the closure is now stored in the function type. With this upgrade, it is now possible to write Juniper programs that use higher-order functions and require 0 heap allocation. Closures are written very similar to records, except that they are enclosed in pipes "|". The new syntax for function types is now (closureTy)(param0, ... paramN) -> ret. As an example, the type of compose is <'a,'b,'c,'closureF,'closureG>(('closureF)('b) -> 'c, ('closureG)('a) -> 'b) -> ((|f : ('closureF)('b) -> 'c; g : ('closureG)('a) -> 'b|)('a) -> 'c). This is Juniper's solution to the funarg problem. The type inference engine should be able to automatically infer these closure types, which means that explicitly writing out the types is for the most part unnecessary.
Number literals without a suffix are now polymorphic, like in Haskell. In particular, an integer literal has a polymorphic type and also generates an additional constraint that it is an int. Similarly a floating point literal is also of polymorphic type but with a constraint of being a floating type. In most cases the type checker ends up constraining these literals to specific numerical types. However it is possible that it doesn't, in which case a "too much polymorphism" error may be generated where an internal expression has a polymorphic type that is not constrained by an input or output type.
Juniper 2.3.0 Released
June 2, 2020
Juniper now uses the Arduino setup and loop functions instead of using a custom main function.
This fixes a long standing bug where programs would shut down unexpectedly after a certain time period.
Instead of a single main function, Juniper now uses a setup and loop function, much like C/C++ Arduino programs.
FARM Presentation now on YouTube!
November 22, 2016
Juniper 2.2.0 Released
November 22, 2016
Bug fixes for type inference
Added more useful functions to the Signal standard library module
Juniper 2.1.0 Released
October 14, 2016
Strings and character lists added.
Print to console functions added.
Pipe syntax added.
Improved type inference. Type variables no longer need to
be explicitly declared to create generic functions.
Instead the necessary type variables can be directly
September 24, 2016
Juniper will be presented at the ACM SIGPLAN International
Workshop on Functional Art, Music, Modelling and Design (FARM) in
The presentation will provide a brief overview of the Juniper
language along with typical Juniper design patterns. Juniper will
be compared to the existing Arduino language of choice - C++.
Existing Juniper projects will be demonstrated. In the spirit of
the Maker Movement, there will be a "hands on" portion of the
presentation, in which audience members will receive (at no cost)
an Arduino compatible microcontroller and the components necessary
to build a small project. By the end of the presentation, audience
members will be able to construct the circuit and write the
program for this project in Juniper.
Performance improvements: compiling a project can now be done
July 15, 2016
Our paper on the Juniper programming language was accepted to the
ACM SIGPLAN International Workshop on Functional Art, Music,
Modelling and Design (FARM)! The pre-print copy of the paper can
be downloaded here:
This paper presents the design and implementation of Juniper:
a functional reactive programming language (FRP) targeting the
Arduino and related microcontroller systems. Juniper provides a
number of high level features, including parametric polymorphic
functions, anonymous functions, automatic memory management,
and immutable data structures. Also included is a standard library
which offers many useful FRP signal processing functions. Juniper is
translated to standard C++ and compiled with the existing Arduino
development tools, allowing Juniper programs to fit on
resource-constrained devices, and enabling seamless interoperability
with existing C++ libraries for these devices.