6 ms·
Arduino-copilot makes it easy to use Haskell to program an Arduino
- jonny383 7y agoTrying to keep an open mind here about this - but what does Haskell offer on an _arduino_ aside from developer preference? Surely C/C++ will be the better performer on such a tiny board... And usually you want to squeeze every drop of performance out of tiny boards
- kryptiskt 7y agoIt's not running Haskell on the Arduino, you are building a C program for Arduino using a DSL in Haskell.
- jonny383 7y agoThe argument is still kind of the same. What benefits does this have (aside from developer preference) over using flat out C/C++? What is the performance like using the DSL in Haskell?
- tartoran 7y agoNot the OP but I suspect we’re not talking about any performance gains here, the arduino is still running binary. What you get by using a functional language like haskell is a way to structure your code differently and in many cases that could make your life a whole lot easier when you translate the solution that you have in your head to code. It is also easier to catch bugs
- np_tedious 7y agoIs developer preference not important? What advantages does C have over processor specific assembly?
- random314 7y agoDeveloper preference is a very big deal. Almost all other factors are secondary.
- pkolaczk 7y agoIf a developer wants to use a hammer for everything, because everything looks like nails to him, then I doubt in his professionalism. Sure, dev preferences matter, but only within the space dictated by the client requirements. Customers do not care if the thing they are using was created with C or Haskell. They care about price, performance and features. It might be harder to program in C++, but the final result can better fulfill the requirements.
- random314 7y agoDevs prefer one language over the other, because they consider it to be better for their use case - it is not because of religious reasons. Clients typically don't have the technical expertise to dictate what technology the dev should use. If the client knows what technology they need, they should simply look for those devs who have expertise in that technology. Half the languages that you see succeed today(the other half are those pushed by megacorps as the only developer platform), are because of dev preferences - not because of client requirements.
- pkolaczk 7y agoYet they often choose wrong tool for the job. We often see games written in Java, big websites created in PHP, or processing huge amounts of data in Python. Here someone wants to do embedded in Haskell. These are not technical reasons. The reasons for these choices are frequently "this is the only tech stack I know" or "this is easier to learn".
- random314 7y agoFacebook has been written in php, and minecraft in Java - because that was the language that the developer preferred. Are you saying this was wrong? Both developers made billions of dollars from it. If a client needs a particular technology then he should look for specialists in those technologies. Otherwise, just leave it to the developers preference. And reasons such as 'this is the only tech stack I know' or 'this is easier to learn' are perfectly good reasons. Reasons for a lot of things are not technical. You think Mark Zuckerberg created Facebook for technical reasons? In fact familiarity with a tech stack and simplicity of learning are seen as key technical considerations when choosing a technology for development. You must be very immature to think these are not technical reasons.
- WJW 7y agoThe advantage of CoPilot (the DSL this library is built upon) is that it builds verifiable constant-time constant-memory programs, which is quite important in some cases (like aerospace control programs). Whether that is an important property for Arduino is debatable of course.
- dragonsh 7y agoYou are right C is a good choice for arduino, but if Python can be used with it, I believe Haskell can also be used in similar way. It will bring many high level features like type safety, immutability, runtime error free code and code that is easy to refactor, once the Haskell learning curve is climbed. I like Haskell for its brevity and pseudo code like syntax which really runs like Python with all the necessary safety. Given Haskell share in the overall eco-system of languages, I don’t expect it to have any significant impact. But this little steps are necessary to move forward Haskell. I think the biggest stumbling block Haskell need to overcome is to have good documentation like Python. I believe if instead of just features, language research Haskell community needs to put extraordinary efforts in documentation. In spite of Haskell being an old language more than 50% of packages on hackage do not have proper documentation and unless that do not go up to 90% or more Haskell won’t be able to be used effectively by programmers to solve computing problems. We all stands on giants of shoulders and for that to happen need documentation. I believe people underestimated the power of good documentation. It should be twice or thrice more efforts than the language feature development itself.
- jonny383 7y agoGreat points, thanks.
- droithomme 7y ago> if Python can be used with it Which Arduino models are people running Python on? Surely not the most common models?
- StavrosK 7y agoNo, the Uno has nowhere near enough RAM to be useful with Python. However, MicroPython runs beautifully on an ESP8266, which everyone should always use instead of an Uno, in my opinion.
- droithomme 7y agoI run Lua and compile C on my ESP8266's. I'm sure MicroPython could fit there as well. But even MicroPython doesn't run on a Arduino Uno, much less Haskell.
- deleted 7y ago[deleted]
- toast0 7y agoPerformance may not matter that much, depending on the application. I've got some arduino capable boards monitoring temperatures once a minute, but I'm using lua because the documentation for my board was less handwavy for lua than for arduino (it's an esp8266 board; the c environment is a little weird).
- pkolaczk 7y agoIf performance doesn't matter, then most likely this is a sign of using too powerful hardware for the job. This is ok in a hobby project, but if you wanted to produce and sell these on mass market, then your competitor would get a smaller board with a less powerful chip and win by the price (or earn more per unit sold).
- magicalhippo 7y agoTrue, though wifi and ethernet are quite demanding, so any uC capable of either will most likely be "too powerful" for things like doing simple I2C/SPI communication with a sensor or five and relaying it back.
- one-punch 7y agoMy understanding is that the domain-specific language (which is embedded in Haskell) generates C code, which is then compiled to an arduino binary. The key sentence: > This whole example turns into just 63 lines of C code, which compiles to a 1248 byte binary, so there's plenty of room left for larger, more complex programs. What the eDSL (embedded domain-specific language) offers is a higher level of abstraction: by using functional reactive programming, the programmer can reason about the code at a more intuitive level (instead of C, assembly, or machine code). Usually people call this style of programming declarative instead of imperative: focusing on what to do instead of how to do it. In a more interesting example in the article, we see right away that the LED is on when, and only when, the button is pressed or when it is “blinking”, with “a longer and longer” delay (with lower level details deferred to the counter function, or with the timing logic handled by the functional reactive programming framework—to appreciate the abstraction ability of FRP, try to imagine how to implement the same LED (which is on when pressed or with a longer and longer delay) in other procedural languages, likely different concerns will be interleaved). When concerns are cleanly separated in Haskell, we understand what the code intends to do right away, making the code more maintainable. (And more fun to read and write!) As a side bonus, Haskell has a strong and expressive type system, and a pervasive use of laziness to enable purity, allowing local equational reasoning (e.g., no need to worry about hidden mutable global state, so functions could be understood on their own, etc.), making the code even easier to reason about and to maintain then some other procedural languages (what you write is what you intuitively mean). So I think the trade off is between developer productivity and performance (of the generated C code vs a handcrafted C code; very much like the trade off between a compiled machine code vs a handcrafted machine code).
- mindslight 7y agoWhat does C/C++ offer aside from developer preference, when compared to assembly? Squeezing every drop out of microcontrollers likely includes much bit-banging. Once you've given up perfect instruction cycle counting, they're all high level languages!
- pjmlp 7y agoMany of those tiny boards are better than most 60's computers, which had quite a few high level languages to choose from, before C was even born. So keeping an open mind is actually the key factor here.
- joeyh 7y agoIt's worth looking at the C code that's generated from a simple blink example in haskell. It's not very idiomatic, but neither is it very slow. Much of the fluff should get optimised away by the C compiler. I've been generally very impressed with how readable the output is, as generated code goes, and how performant it seems. #include <stdint.h> #include <stdbool.h> #include <string.h> static bool s0[(2)] = {(false), (true)}; static size_t s0_idx = (0); bool s0_gen(void) { return (s0)[s0_idx]; } bool delay_guard(void) { return true; } int16_t delay_arg0(void) { return 100; } bool digitalWrite_guard(void) { return true; } int16_t digitalWrite_arg0(void) { return 13; } bool digitalWrite_arg1(void) { return (s0)[s0_idx]; } void step(void) { if ((delay_guard)()) { (delay)(((delay_arg0)())); }; if ((digitalWrite_guard)()) { (digitalWrite)(((digitalWrite_arg0)()), ((digitalWrite_arg1)())); }; ((s0)[s0_idx]) = ((s0_gen)()); (s0_idx) = ((++(s0_idx)) % (2)); } void setup() { pinMode(13, OUTPUT); } void loop() { step(); }
- joeyh 7y agoDoing some constant folding, the C compiler would arrive at something like this: void loop(void) { delay(100); digitalWrite(13, s0[s0_idx]); s0[s0_idx] = s0[s0_idx]; s0_idx = (++s0_idx) % 2; } void setup() { pinMode(13, OUTPUT); } Only the s0[s0_idx] = s0[s0_idx] looks like extra work now. I don't know if a C compiler would optimise that away, but assuming it does not, one extra memory write per loop is not bad overhead.
- kbumsik 7y agoI'm not familiar with Haskell and functional programming in general, so I'm curious if these codes in the OP make sense to Haskell people? Every lines of the codes are full of side effects (LEDs, buttons, delay...) and completely procedural.
- monoideism 7y agoPardon the off-topic question, but I noticed you used the plural "codes" instead of "code", which has typically been used in American English. Traditionally, "codes" has been used in other dialects of English, but I've noticed an increasing number of Americans using the plural form. Is this a thing now? Are we saying "codes" instead of "code"?
- kbumsik 7y agoInteresting question. I'm Korean and actually I was asking myself if "codes" is correct English while typing. I don't know, but except for grammar "code" seems to be countable to me. Note that Korean grammar (and maybe some other languages) doesn't have strict rules about plural forms. Anyway "Codes" make sense to me when they refer "snippets". edit: added some more thoughts.
- Dansvidania 7y agoCode as in 'sourcecode' is uncountable IMO. Code as in 'password' or 'cypher' or 'set of symbols' is instead countable.
- monoideism 7y agoYes, I think it's something some American developers have picked up from others using "global" English. I heard the plural used a lot from devs in and from the Indian subcontinent, and now, I'm starting to hear native American devs use the plural form.
- one-punch 7y agoThe code is mostly pure Haskell (the do block, which most Haskellers are familiar with), with a few domain-specific elements (those led and delay “assignments” and the Stream type in this functional reactive programming framework arduino-copilot, whose meaning is specific to this Arduino application and most Haskellers have to guess). The short answer is that I could guess what it means. This `arduino-copilot` example really showcases one killer application of Haskell: writing eDSL (embedded domain-specific language) in Haskell. Haskell allows really high level abstraction: in particular, you can overload the operator which combines different statements in Haskell. In C/C++, the semicolon combines subsequent statements, Haskell even allows you to overload (give a new meaning to) the semicolon “operator” (think of overloading ; instead of overloading +). That is, in Haskell we can interpret differently how to combine subsequent statements in different contexts (such as null-checking, exception handling, async/await style concurrency, looping, or procedural state passing, etc., see the example below). However, when we “overload the semicolon operator”, we intuitively have some expectations: 1. We expect to be able to turn a value into a standalone statement (to inject a value as a single statement with that value, e.g., to turn the value “97” into the statement “97;”, and this operation is called return) 2. We expect to be able to use the result of a statement in the next statement as in str <- getLine; putStrLn str This could be rewritten using a bind operation (denoted >>=) as getLine >>= putStrLn. When we package up (encapsulate) the above two operations (return and bind), we expect the two operations to play nicely with each other to satisfy some intuitive laws (e.g,. if you inject a value then use the value in a subsequent functional statement, it is the same as just calling the function on the value, so the return and bind cancels out, etc.). A mathematical formalization of this idea (the two operations with some intuitive laws) is named a monad, which happens to generalize many different computational contexts of combining statements such as null-checking, exception handling, async/await style concurrency, looping, or procedural state passing [1]. [1]: https://philipnilsson.github.io/Badness10k/posts/2017-05-07-escaping-hell-with-monads.html https://philipnilsson.github.io/Badness10k/posts/2017-05-07-... The ability to reinterpret statement compositions is why Haskell is so great for embedding domain-specific languages (even procedural eDSL), as the arduino article shows, and this is why some people call Haskell the finest procedural language.
- ph2082 7y agoThat looks pretty cool. Is there some comparison table exists about binary size, memory usage etc against other languages?
- yspeak 7y ago.>"It was developed for NASA by Galois and is intended for safety-critical applications. So it's neat to be able to repurpose it into hobbyist microcontrollers. (I do hope to get more type safety added to Copilot though, currently it seems rather easy to confuse eg miles with kilometers when using it.)" I do hope that the km/m confusion is in the NASA version.
- wruza 7y agoWhen I first met arduino (and quickly more embedded boards afterwards), the hard part was not C[++], nor a toolchain around it – I’m a C guy since ‘00. It was the fact that this area has few de-facto standards on how things should cooperate. Basic tutorials have no notion of event loops, you just for-loop forever and poll pins. But once I’ve got few non-standard devices, like that pressure sensor I got somewhere in the city, it all falls apart, because e.g. the time required to talk to it via provided library consumes enough cycles for your entire process to be late on physics. And vice versa, your logging is not async and interferes with signal timeouts. You simply spend tens-thousands of cycles spin-locking on i/o and there is no way to fix that without rewriting “drivers” under some “os”. Idk if haskelling everything could help with that. I decided to just move on to high-mhz stm’s or even -pi variants, where a whole different stratum of software lives (seemingly), but luckily project folded for unrelated reasons. My conclusion was that if you’re a software developer, don’t just go embedded, it is not what you used to and routes you can take are limited. Edit: obviously missing some details here, and it was 7 years ago, would like if someone would elaborate on the state of the embedded world today.
- joeyh 7y agoAuthor here.. Just as a preview of the next release of this, I've been teaching it how different pins of the arduino can be used. Some pins support digital IO, some also PWM, some analog input pins also support digital IO, but others don't. So I can write a program like this and pick pins to use essentially at random: import Copilot.Arduino.Uno main :: IO () main = arduino $ do pwm pin2 =: longer_and_longer delay =: constant 10 And without even needing to load it into a board, let alone hook up wires, the compiler can tell me it won't work: demo2.hs:5:9: error: • This Pin does not support PWM • In the first argument of ‘(=:)’, namely ‘pwm pin2’ In a stmt of a 'do' block: pwm pin2 =: longer_and_longer This is implemented using type level lists and type level programming, and user-defined type error messages.
- gregwebs 7y agoThis looks like a great system to simplify Arduino programming. However, this runs your program in a loop. You can add a delay to reduce power consumption, but it will reduce responsiveness. I am really looking for interrupt-based Arduino programming.