Everyone loves learning a new programming language, right? Well, even if you don’t like it, you should do it anyway, because thinking about problems from different perspectives is great for the imagination.
Juniper is a functional reactive programming language for the Arduino platform. What that means is that you’ll be writing your code using anonymous functions, map/fold operations, recursion, and signals. It’s like taking the event-driven style that you should be programming in one step further; you write a=b+3
and when b
changes, the compiler takes care of changing a
automatically for you. (That’s the “reactive” part.)
If you’re used to the first-do-this-then-do-that style of Arduino (and most C/C++) programming, this is going to be mind expanding. But we do notice that a lot of microcontroller code looks for changes in the environment, and then acts (more or less asynchronously) on that data. At that level of abstraction, something like Juniper looks like a good fit.
Changing up the programming paradigm for Arduino is an ambitious project, especially considering that it was started by two undergraduates [Caleb Helbling] and [Louis Ades] as a senior design project. It’s also brand new, so there’s not much of a codebase out there yet. Time, and your participation, will tell if it’s useful. But one thing’s for sure, once you’ve programmed in a reactive language, you’re not going to be able to look at a delay loop the same again.
What’s the wierdest language you’ve ever programmed a microcontroller in?
(The XKCD comic’s alt-text reads “Functional programming combines the flexibility and power of abstract mathematics with the intuitive clarity of abstract mathematics”.)
Quick, somebody write 99 Bottles of Beer in this language!
I’m not sure how well functional programming will work in the constrained performance environment of an AVR. With C, I can look at a block of code and make a good guess as to how many cycles and how much memory it’ll use. I don’t know if that’s true in a functional language.
That was my initial thought when reading this… I suppose time will tell what the performance is like.
It’s more than that, if you think about it. OK, so the performance might be limited. Whatever. But the bigger question is *how exactly does it get implemented*?
C exists because, in general, it maps “pretty well” to the basic operation of most computers. Not perfectly (no concept of carry/zero/overflow flags, which just about every computer has) but pretty well. You’ve got jumps and branches, often times you also have computed branches (which are the equivalent of switch statements), and calls and returns. You have temporary storage (registers), and permanent storage in an addressable array. Pretty basic.
Functional programming *does not* map to the basic operation of computers at all. It maps better to how we *want* them to work (if this happens, do this, if that happens, do that) but they don’t *actually* work like that.
So, for instance, in their Blink example, the “Io:toggle” function gets called every time the “timerSig” event gets emitted. Great. Fantastic. How does that get implemented? Is it in an interrupt? Is there a chance Io:toggle might miss a timerSig event (e.g. is it queued, or is there a flag)? How many functions can be tagged to a timerSig event? What’s the scaling in terms of resource cost?
And really, there’s *no information* on how it gets implemented at all. Which, really, is very frustrating, because without knowing the environment’s limitations, it’s hard to imagine working within them.
The documentation for the Io:toggle function is here:
http://www.juniper-lang.org/docs/files/Io-jun.html#Io.toggle
If you really want to get down and dirty you can view the source code of the standard library directly:
https://github.com/calebh/Juniper/blob/master/Juniper/junstd/Io.jun#L82
As you can see, the Io:toggle function pattern matches on input (which has type pinState). When the input is the low value constructor, the high value constructor is used to make the return value, and when the value constructor is high, the low value constructor is used to make the return value.
http://www.juniper-lang.org/docs/files/Io-jun.html#Io.pinState
https://github.com/calebh/Juniper/blob/master/Juniper/junstd/Io.jun#L34
As for how Juniper works:
Signals are secretly maybe values. The definition of a signal is very simple and can be found in the Prelude:
http://www.juniper-lang.org/docs/files/Prelude-jun.html#Prelude.sig
https://github.com/calebh/Juniper/blob/master/Juniper/junstd/Prelude.jun#L112
All signal processing functions are just normal functions lifted into the Maybe monad! You can see this in action at
https://github.com/calebh/Juniper/blob/master/Juniper/junstd/Signal.jun#L25
So signals are actually ordinary values, just like integers or any other types.
Juniper does not use interrupts anywhere at this point (getting the compiler to work was hard enough). Everything uses a polling based model.
In some other FRP languages you might see an explicit separation of a language describing the signal graph or a signal graph might be constructed as a in-memory directed graph. This is not the case in Juniper – since signals are simply maybe values, this means that there is no signal graph constructed at any stage. Instead, the information about the signal graph is encoded in the call graph!
We haven’t had a whole lot of time to work on documentation. Currently I am spending my time writing a paper on the language and haven’t had time to work on another tutorial.
-Caleb
No interrupts?
Does that mean, no UART, I2C, ADC, timers?
Yeah, I’m a bit confused on the whole “no interrupt” thing. I can buy that for the Time portion, although it looks like, from the Time code, that if the system is busy, and the timer isn’t polled fast enough it won’t be regular (since it updates lastPulse to t, not to an increment of the current time).
I’ll be honest, I’m always confused as to people who pay attention to *language* semantics more than *how the language gets implemented*. It doesn’t matter how powerful the semantics are if the implementation doesn’t work well.
You’ve said before that it compiles to actual C++ code, right? It’d go a long way to helping people understand how the language works if you’d just post the compiled C++ code alongside the examples.
How things get implemented in a language is very important, even beyond the micro to the desktop. Take this example from .NET, https://blogs.msdn.microsoft.com/ericlippert/2009/11/12/closing-over-the-loop-variable-considered-harmful/
He who limits their self will be that that which thy limits one self to(do not ever limit yourself for that will be all you will ever be)
…and he who limits their self not at all shall be called “bloatware”.
So…..it’s a spreadsheet?
Yes, you can think of the language as a very very advanced spreadsheet.
One of main selling points of the Arduino environment is the plethora of libraries. Looking at the Juniper documentation, I couldn’t figure out if you could access the usual libraries, e.g. I2C, SPI, etc. I realize this is project done by two undergrads (nicely done BTW), but this is of limited utility if the only interfacing with the outside world is purely through bit twiddling.
There is a section in the language docs about interfacing with existing C/C++ libraries. Since Juniper compiles to C++, you can even write inline code in the language.
http://www.juniper-lang.org/language_docs.html
(Interacting with C++ Libraries section, at the bottom of the page)
The nice thing about the AVR is that all of that is accomplished by reading/writing a limited number of registers. However, rewriting all of the nice wrappers that the Arduino folks have assembled strikes me as an unpleasant task.
I dunno about that.. in most cases the arduino implementation is vastly suboptimal due to questionable ease of use choices. The nice thing about AVR is that all the chips have tons of commonality… so even if you wrote some assembly for a teensy it wouldn’t be too hard to port it to a larger one if you needed to include more features. So, while it’s true that it would be a decent amount of work… it would be fairly rewarding work for many people.
Not that I write any assembly… just C and I’m particularly fond of my 350~ byte quadrature encoder for ATtiny13A I’d be curious if anyone could do it in less but wouldn’t be too surprised.
This is precisely why the author of the post is asking for community help!
There is a section in the language docs about interfacing with existing C/C++ libraries. Since Juniper compiles to C++, you can even write inline code in the language.
http://www.juniper-lang.org/language_docs.html
Interacting with C++ Libraries section, at the bottom of the page
Isn’t that just a software equivalent of Interrupt-On-Change routine mixed with finite state machine? It can be done with a switch-case statement in main loop and functions that actually do stuff called from it, with added functionality of no overhead…
>What’s the wierdest language you’ve ever programmed a microcontroller in?
I don’t like juniper. how about avici?
(old telecom joke; now, GOML)
I don’t think we need YAPL (yet another programming language). Good enough is good enough. As someone who has programmed in Forth and has been trying to understand how C libraries interface to Lua, I nearly always come back to the well know languages for text based programming. I do think there is room for new programming paradigms but it is difficult to balance easy initial coding, understandability (being able to read the code and understand what it does), ease of debugging and efficiency. The tried and trusted languages have a pretty good balance.
So basically “what we have is perfect, so progress is unnecessary”?
He didn’t quite say that. And while I hesitate recommending Forth in the 21st century, looking at the ‘blink’ example, I’d rather program that in Forth.
Assembler, Forth (and to a lesser degree) C, break down in large projects or large (for Forth and Assembler larger than one or two really) teams. Since there won’t be large projects on Arduino, that’s not a problem in this space.
Functional programming languages have their space and use, but I’m not convinced that 8 bit microcontrollers is where it’s at.
Juniper? really? is is IOS compatible?
totally not confusing
…wellfleet and proteon were unavailable for comment ;)
Hey, y’all know about PLCs, right? Ladder logic? It’s mostly just a clunky (being aimed at electrical people, not software people) way of expressing outputs as functions of inputs.
If you want to get an automation system certified as being safe, you pretty much have to use those functional kinds of technologies. No one trusts imperative code.
The problem with ladder logic is it has limited scalability… you’d have to be a complete madman to design the code we write at my job in ladder. That said… safety critical things (like emergency stops) are often done in ladder just because it’s what the electricians understand.
So in essence you end up with a PLC with control logic written in structured text or what have you and all the fault handling logic in ladder. So you get to have your complex logic while offloading any needed “trust” to the ladder logic.
Can’t a state machine do the same?
Just read through the intro and don’t see anything that cannot be done in modern C++ and the code would look just about the same. Modern C++ has lambdas which seems the only trick they are playing in this code. I’m willing to be enlightened.
Modern C++ does not have algebraic data types and pattern matching.
I visited a couple web sites on algebraic data types. They didn’t help. Didn’t follow upon the pattern matching. But I’ll be surprised if this is something really new that I haven’t seen before in all my decades. LOL
You might be surprised then, and learning something new about languages can help you even in your preferred language. Pattern matching in particular is a very nice concept/experience. Perhaps you could learn Rust or SML to get your feet wet?
Pattern matching isn’t new. I can go back to SNOBOL for it. That’s text matching but the concept is the same. For that matter C++ signature matching is the same concept.
I suspected templates provide the algebraic data types but wasn’t 100% sure. Your other comment indicates they do.
Actually, the C++ template language has that and more. In C++ you even have matching on type-integers, in the template language, and type specialization. I bet Juniper doesn’t let you do those. (C++’s handling of union types is lackluster, I admit)
The word you’re looking for here is ad hoc polymorphism, which Juniper does not currently support. There are other functional languages such as F# and Elm which do not have support for ad hoc polymorphism but are both extremely usable nonetheless.
Juniper does have some support for integers in templates; see the section on capacities on http://www.juniper-lang.org/language_docs.html
In functional languages there are a couple different ways to handle ad hoc polymorphism. The first is via modules (SML and OCaml) and the second is type classes (Haskell). After the more advanced Hindley-Milner type system is implemented in Juniper I will take a look at implementing type classes.
To mime the GOML dude @bl from earlier…
Whether polled or interrupt driven, aren’t we still logically discussing just another way of doing:
If then
Just because you have fancy types, pattern matches, etc…isn’t it just another form of abstraction but just a little further removed from machine code? You just aren’t referencing/checking the condition in the code, though polling something isn’t far removed IMHO.
Like @W though, I’m willing to be enlightened as well, but haven’t yet seen reasons to move away from procedural for most things.
How are you supposed to debug a problem with the data-flow timing through a graph when sub paths are different lengths and the signals linked to an eternal interrupt are not synchronised such that a=b+c but the states of b and c each have a time line (obviously) but due to the complexity required to calculate c compared to b they get out of sync so that a = b.time(x) + c.time(x+d) where d is the difference between the execution times for computing b and c when you really wanted a = b.time(x+d) + c.time(x+d) and even then what if b is rapidly changing, faster than c can be computed.
Hmmm… eternal interrupts… those are the ones that forget to clear the interrupt flag, right? ;)
Not that that has anything to do with my question which describes a form of race condition problem.
How about you do the decent thing and attribute or link back to the XKCD cartoon you ripped the image from?
Fair-use, right?
Sounds to me like they are trying to make a MCU behave like an FPGA. It seems like it would be more efficient to map this to Verilog or VHDL and use small FPGAs like the ICE40 series.
I managed to get Erlang going on a Sharp Zaurus a long time ago. It ran, sort of, but I had to remove a lot of the libraries to fit it into the memory. There is no reason why functional languages should not run on low resource systems and Erlang being soft realtime could be quite useful. The biggest issue would be memory management since badly written applications can take up a lot or memory in recursion, so a simple OS may crash and burn
C and C++ still stands, the other ones seems like winds going by…
I really, really like the idea of this language. The idea seems a perfect fit for the Arduino platform at AVR related work. I could see this language being exceptionally useful for small IO work. Not to replace tried and true languages, but as an alternative for appropriate situations. I am also impressed by the makers, great work, from new developers taking on ambitious projects. I realize that you can probably do everything the language does in other mature languages (as pointed out by nearly every comment), but Juniper is not replacing the work done in huge software companies. I feel irritated by the overall negative tone and lack of support in the comments, although I may be reading into skepticism too much. Well done Caleb Helbling and Louis Ades!
-Cheers!
“I could see this language being exceptionally useful for small IO work. ” Why, exactly?
could you give an example of an ‘appropriate situation’ ?
I dont tginj the majority of tone is negative, just people genuinely asking ‘why’?
I am a logic kind of guy so i like logic programming.
As such I wonder what is it I can do with Functional reactive Programming that I cannot do in C?