Juniper

Functional Reactive Programming for the Arduino

View the tutorial » Install the compiler» Read the paper»

Lightweight and Effective

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.

Features

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.

Latest News

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

New features:

  • 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

New features:

  • 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

New features:

  • Bug fixes for type inference
  • Added more useful functions to the Signal standard library module

Juniper 2.1.0 Released

October 14, 2016

New features:

  • 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 inferred.

FARM Presentation

September 24, 2016

Juniper will be presented at the ACM SIGPLAN International Workshop on Functional Art, Music, Modelling and Design (FARM) in Nara, Japan.

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.

The source code for the demo project can be found here: https://github.com/calebh/SoundBar.

Here are the PDF versions of the FARM presentations:

Juniper 2.0.0 Released

September 23, 2016

New features:

  • New type checker: type inference now supported!
  • New parser: improved parsing error messages.
  • Performance improvements: compiling a project can now be done nearly instantaneously.

FARM Paper

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: juniper_farm_preprint.pdf

Abstract

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.