Qqwy
Hey all,
This is a small and simple library I am developing, that will be part in a larger whole of libraries to hopefully at some point allow nice, functional native Graphical User Interfaces to be made from within elixir.
People who know about Elm: This is basically what Elm does as well.
GenFRP
GenFRP is a library that allows for programming in elixir using the
Functional Reactive Programming method.
This is a way to store and dispatch information that is very readable and extensible.
The idea is as follows:
- Your FRP module contains
use GenFRP, initial_state: some_useful_starting_state. (some_useful_starting_statecan be anything you wish, of course) - It implements the
update(state, event) :: statefunction. - It implements the
render(state, last_rendered_state) :: anyfunction.
update is called whenever an event is sent to the GenFRP process by using GenFRP.send_event(frp_pid, whatever_you_want_to_send).
render is called whenever you want to know its current state (in some representation format that is useful for the outside world, i.e. rendered).
GenFRP uses a very simple caching mechanism to ensure that render is only invoked when the internal state has changed since its last invocation.
Here is a very simple example (which can be found as GenFRP.Example.Map as well)
defmodule GenFRP.Example.Map do
use GenFRP, initial_state: %{}
def update(state, [{key, val}]) do
Map.put(state, key, val)
end
def update(state, event) do
IO.puts "Unrecognized event passed to `#{inspect(__MODULE__)}.update/2`: #{event}"
end
@doc """
Returns the key-value pairs as strings, separated over multiple lines, in alphabetical order.
"""
def render(state, last_rendered_state) do
Enum.map(state, fn {key, val} ->
"`#{key}`: `#{val}`"
end)
end
Enum.join("\n")
end
This can be used as follows:
{:ok, pid} = GenFRP.start_link(GenFRP.Example.Map)
# ... maybe some other code here...
GenFRP.send_event(pid, {:foo, :bar})
# ... maybe some more code here...
GenFRP.send_event(pid, {:baz, 42})
# ... maybe yet some other code here...
GenFRP.render(pid)
"`foo`: `bar`
``baz`: `42`
"
This is the first, 0.1.0 version release. I wanted to get this out quickly, to get some feedback from the community.
Expect things to be still somewhat unfinished, and don’t worry: I will run Credo on the code to make it nicer
.
But: any feedback is greatly appreciated. Is this programming interface understandable? Are there things that the library is glaringly lacking?
Thank you,
~Qqwy
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










First Post!- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
michalmuskala
I don’t see a link to the repo, I think you missed it
Looking at the example, I’m not sure I understand the decision to pass the initial state to the
usemacro. Wouldn’t it be better to follow the example of all the OTP behaviours and have aninit/1callback for computing the initial state? It doesn’t require the initial state to be a compile-time constant, like the current solution does.Most Liked
StefanHoutzager
Nice! I don’t know about frp, so I’m reading about it now. Maybe interesting for others also
Here is a short intro (look for the copied text above to read the whole text): http://stackoverflow.com/questions/1028250/what-is-functional-reactive-programming
OvermindDL1
This could be used as the basis to make an elm’y clone in ElixirScript for both server-driven and client-driven page handling. ^.^
Now if only a type system. ^.^
Qqwy
Yes, it was… (I need to get some sleep).
Yup, that would be amazing. I’m not sure how far ElixirScript is right now with supporting processes; there were some caveats earlier if I remember correctly, but yes, I am sold on this idea
.
As for the native GUI idea: I’m now working on a semi-direct (and thus stateful) wrapper to
:wx_widgetscalledWex(which is far from finished, because:wx_widgetsis HUGE, and there are some design choices that still need to be made). Another to-be-written library that has the WIP-nameCandlelightwill wrapWexwith its own structs that are stateless representations of a certain GUI.Candlelightwill use MapDiff to check for changes in theis GUI representation, and run the requiredWexfunctions to update the changes.To the end user, this means that a GUI can be constructed and altered by nesting the
.
Candlelightstructs (or functions/macros(?) that create them) in a very simple and clear manner.Very similar to how Elm allows the creation of HTML content using its HTML DSL. However, GUI elements in WxWidgets are a lot more complicated than a HTML representation (which is just ‘text’; we let the browser figure out how it should look) so there is a lot of work to be done
Last Post!
Qqwy
I’ve had some time today to update GenFRP to a new version, which uses the
init/0function instead of the olderinitial_state/0function (whose default implementation uses theinitial_state:key that is part of theuse GenFRPstatement). It’s much clearer now, as it works the same as GenServer and friends.The new version is 0.5.0; I expect that there are not a lot of changes that need to be done before releasing a stable 1.0 version (to be more exact, I think the current publicly exposed function names will remain stable).
As most of Elixir/Erlang is built using functional patterns, nearly all places can be handled by just wrapping something and then calling
send_eventdirectly.But in the case something depends on some externally-stored state, the Callback module might be used instead. Right now
GenFRPuses thePeticklibrary to allow callbacks that fire every x milliseconds. This is built-in as I believe that this is a very common thing that people will want to do.I am wondering however if there are more things that GenFRP could/should provide built-in callbacks for.
This is the single thing I’d like to be sure of before releasing a 1.0. Any suggestions are highly appreciated!
(I hope this post is coherent; I just got back from the Global Game Jam and slept very little these last 50 hours…)