Arduino-copilot makes it easy to use Haskell to program an Arduino
joeyh.name
joeyh.name
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.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.
Is this a thing now? Are we saying "codes" instead of "code"?
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.
Code as in 'password' or 'cypher' or 'set of symbols' is instead countable.
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-...
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.
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
What advantages does C have over processor specific assembly?
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.
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.
> You must be very immature to think these are not technical reasons
Sorry, but this ends the discussion for me. I thought we were discussing on merits, not ad hominem arguments.
You are making immature statements like - Time to learn a technology and familiarity with a stack are not technical considerations. I expect a junior engineer to make such statements but not a senior engineer or a hiring manager. These are critical technical considerations when choosing a technology. Your statements show immaturity - period. If a director of engineering asks an engineering manager if he will consider the tech stack familiarity and learning curve for the team when choosing a tech stack and the manager responds in the negative, trust me - he will start looking for a new engineering manager. You are fixated on picking the "right technology" while your team loses momentum and attrition sets in. You sound like you think your technical decisions are unbiased, but Zuckerbergs decision to use PHP was biased. It is obvious why Zuckerberg used php. He was familiar with php and that was the fastest way for him to build a website. But according to you he made a terrible technical decision and he should have used something else. This is an immature statement. BTW, Facebook still runs on php.
A piece of advice, do not try to impose your biases - technical or otherwise on other engineers. They will do what they want to do. If you join a team and they do not share your technical biases - you have 2 options 1. Show them why the technical choices you propose will help those developers in the long run. 2. Join a different team which shares your bias
Badgering them with your opinions is not an option. Your options are - Show the developer why building Arduino copilot is not in his long term interests. The other option is - Galois is making tons of money by building copilot for safety critical embedded projects for NASA. May be you can also talk to NASA and get them to shut down the project. Otherwise you are just wasting your time and everyone else's time.
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.
Which Arduino models are people running Python on? Surely not the most common models?
But even MicroPython doesn't run on a Arduino Uno, much less Haskell.
This is strictly for Arduino/embedded. For general purpose programming? I don't know.
Is there a way other people can contribute to documentation ? Also how to generate documentation like found in Hoogle ?
They are a bit finicky to set up though.
Another good resource: http://yannesposito.com/Scratch/en/blog/Haskell-Tutorials--a...
Here are a few links though:
* https://ndmitchell.com/downloads/slides-drive-by_haskell_con...
* https://haskell-haddock.readthedocs.io/en/latest/markup.html
* https://docs.haskellstack.org/en/stable/build_command/#synon...
Also, maybe join the #haskell-beginners channel on Slack: https://fpchat-invite.herokuapp.com/
There are other platforms too though, e.g. https://www.reddit.com/r/haskellquestions/, https://discourse.haskell.org/
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).
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!
So keeping an open mind is actually the key factor here.
#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();
} 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.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.