Git Product home page Git Product logo

Comments (7)

sedwards-lab avatar sedwards-lab commented on May 26, 2024

My vision was that the environment would be passed in to a program as an argument. Specific to each board, the environment would be an aggregate type (a record) consisting of a type-specific field for every source of I/O available to the program. These fields would be passed around as references and there would be various libraries for type-specific I/O behavior that could be used with these fields.

Something as simple as LEDs would be delivered as a fixed-sized array of references to LED "variables" that, when written, would send the appropriate value to the given LED.

My idea was that we could do this somehow with types. The runtime system is meant to supply two type-specific write functions/macros for each type (e.g., assign_led, later_led) as well as a type-specific update function that's invoked dynamically based on its runtime type (like an OO method or a typeclass). For something like an LED peripheral, there would be custom versions of all of these functions/macros that would get linked with the generated code.

Inputs are more complicated. Again, I intended them to appear as variables passed into the main function. The runtime should collect events from various I/O sources and queue them with a timestamp in an interrupt-safe queue. The runtime should block on this queue and when it sees a pending event, it should transfer the event from the interrupt-safe queue into the normal event queue and call tick().

In some sense, this begins to look a little like the device driver problem/solution in Unix, where devices appear as files, but the operating system instead sees them as major device number/minor device number pairs and dispatches the appropriate read/write functions when a user programs reads or writes to them. Instead of "everything is a file," my philosophy is "everything is a variable."

With your "getButton 1" code, what happens if it's called more than once? Does the "device driver " keep a list of all things that is watching this button? One advantage I can see with your approach is that you force the program to explicitly indicate which I/O sources it actually cares about. In my view, I didn't consider whether particular interrupt sources (really the issue) are enabled or disabled, although it would be easy enough to have some sort of "enable interrupts" function that can be called on the abstract I/O variable for various sources.

from scoria.

Rewbert avatar Rewbert commented on May 26, 2024

My main concerns are that I want a design where we don't need to alter the RTS when we need to support a new peripheral, as few callbacks as necessary are installed (only those that the program requires) and that the programmer in Haskell does not need to worry about a reference really being a LED or just a normal reference with a binary value.

I imagine in my mind that getButton 1 would, the first time you call it, instruct the compiler to do the necessary setup required, installing callbacks etc, and then create a reference that is returned to the programmer. If you now call the same function again it will detect that initialization has already been done, and instead just return the previously defined reference.

This reference could perhaps be declared in the C main function and then passed as a parameter to some scheduler_main, which is the point where our scheduler takes over. The only place where it magically needs to appear is in the function that calls getButton 1. All other procedures will receive it as an argument.

I don't see why we shouldn't be able to use the assign_type and later_type etc functions to capture side effects. I hope that this code can be generated by the compiler. I can think of some hacky ways to do it. I imagine that there would be a module LedModule or something that describes these structures/functions, and when you use it, it generates the correct code. Maybe we can use this to even describe a board in its entirety! I've not thought too much about this, but maybe something like this:

data Board = -- some nice representation

-- | Get an object that you can use to interact with a board
getNRF52  :: SSM Board
-- | Given a board, fetch a reference to the first LED
getLED1    :: Board -> SSM (Ref Bool)
-- | Get a reference to the first button
getButton1 :: Board -> SSM (Ref Word8) -- Word8 for maybe signaling different events (button-press, button-release etc)

When you call e.g getNRF52 you can tell the SSM monadic computation that when it generates code, it needs to generate initialization code for that specific board, and similarly for getLED1 etc. The getLED1 function would emit instructions for how the struct needs to look, definitions for the later functions and so on. You would write this module just once for each board, and then the programmers don't need to worry about this. Adding support for a new board just means writing a new module where you describe the different components required in the generated code.

For now we can just hardcode this to only talk about C code, but ideally this description would be generalized so that we can generate code for different targets. E.g the micro:bit's run JavaScript. Maybe we can write a program with two parts that communicate, where one part runs C on a nrf52, while one runs JavaScript on a micro:bit.

This discussion is good, it makes me think harder about what I am envisioning. Let me know if anything sounds funny or weird.

from scoria.

Rewbert avatar Rewbert commented on May 26, 2024

An upside of being explicit in Haskell about which IO devices you will need is that we can generate code that initializes just that specific stuff :)

from scoria.

sedwards-lab avatar sedwards-lab commented on May 26, 2024

The problem I see with not drawing a distinction between LED variables and ordinary Booleans is the overhead it suggests. Unless I'm mistaken, taking this approach means doing some sort of virtual function dispatch for every variable assignment since you can't know except at runtime whether something is a boring Boolean that can be assigned in the usual way, or whether you need to call a magical I/O-specific function. While you can do the latter by adding a virtual table pointer to each variable that includes a variable-specific assignment function, having to do this to every single variable in the program seems like a lot of unnecessary overhead.

One possible way to address this is to allow something like typeclasses in the language and have something like an "assignable" typeclass that defines an assignment operation. This might take care of the problem since I imagine making type-specific variants of polymorphic code, something like C++ templates but with typing discipline.

Your idea of creating an "I/O reference factory" function, however, is interesting in that it could also address the problem of creating new Bluetooth connections, for instance.

from scoria.

Rewbert avatar Rewbert commented on May 26, 2024

Comparing it to a boolean was just an example, but what we can do is make the 'reference factory' function create a more involved type that actually statically represents all the proper information needed to light the leds etc, but just pretend in the haskell types that it's a boolean. We'd still be able to generate very specific code but the programmer would not need to care about those details.

from scoria.

Rewbert avatar Rewbert commented on May 26, 2024

This is one of the pros about having an untyped AST representaion with phantom types. We can create a value that represents a led reference, but then give it a type that reflects something far simpler.

But again, representing leds with booleans was just an example :) We'd ideally have our own LED ADT types, where we'd have a reference to a led state or something (ON | OFF).

I've prepared some simple code to show on Tuesday! :)

from scoria.

Rewbert avatar Rewbert commented on May 26, 2024

After some thought it is probably good to not explicitly say in the code that you are using a specific board, e.g getNRF52840 :: SSM Board, since this means you would need to change this before running the code on another board. Better is to supply the board as a flag or something at a later stage.

from scoria.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.