<iframe allowfullscreen="" frameborder="0" height="270" src="https://www.youtube.com/embed/JCSxWfUvi6o" width="480"></iframe>
Fast and Slow? An update!For the sakes of simplicity, the original version of the sticky notes program shown in the talk uses an SQLite database to serve the view directly with no in-memory model or caching. This is at least the cause of some sluggishness and at worst pathological performance depending on your machine.
As alluded to in the talk, there is a better albeit more complicated way. This involves moving the slow database accesses onto a separate thread and keeping a fast in-memory model to support the user interface. Changes made by user are now queued up and then flushed to disk asynchronously.
I've now released an updated version (0.3.3.0) of the hsqml-demo-notes package which includes a new faster version of the program illustrating exactly this technique. This hsqml-notes executable is now built using the new code and, for reference, the original code is built into the hsqml-notes-slow executable.
The fast variant uses three MVars to communicate between the UI and database threads. An MVar is kind of synchronisation primitive offered by Haskell akin to a box which may either contain a data value or be empty. Operations on MVars take out or put in values to the box and will block if the MVar is not in the appropriate state to accept or produce a value. Hence, a variety of different constructs such as locks and semaphores can built out of MVars.
The first MVar in the new notes code, modelVar, contains a Map from note IDs to the data associated with each note. This is the in-memory model. It includes all the fields held in the database table plus an additional field which indicates whether there are any pending changes which need flushing to the database (whether the record is "dirty"). The MVar semantics here act as a lock to prevent more than one thread trying to manipulate the model at the same time.
A second MVar, cmdVar, is used as a shallow channel for the UI thread to signal the database thread when there is work to do. The database thread normally waits blocked on this MVar until a new command value is placed in it, at which point it takes out and acts upon it. The first command given to the database thread when the program starts is to populate the model with the data stored on disk. Thereafter, whenever a user makes a change to the model, the dirty bit is set on the altered record and a command issued to the database thread to write those dirty records to the database.
Finally, the third possible type of command causes the database thread to close the SQLite file and cleanly exit. In that case, the third MVar, finVar, is used as a semaphore to signal back to the UI thread once it has shut down cleanly. This is necessary because the Haskell runtime will normally exit once the main thread has finished, and the MVar provides something for it block on so that the database thread has time to finish cleaning up first.
What is the FactoryPool actually for?QML objects require a relatively explicit degree of handling by Haskell standards because the idea that data values can have distinct identities to one another even if they are otherwise equal is somewhat at odds with Haskell's embrace of referential transparency. This sense of identity is important to the semantics of QML and can't be swept under the rug too easily. Crucially, using signals to pass events from Haskell to QML requires that both the Haskell and QML code are holding on to exactly the same object.
One way to accomplish this is to carefully keep track of the QML objects you create in your own code. The factory-pool is an attempt to provide a convenience layer atop object creation which saves the programmer from having to do this. It is essentially an interning table which enforces the invariant that there is no more than one QML object for each distinct value (according to the Ord type-class) of the Haskell type used to back it. If you query the pool twice with two equal values then it will give you the same object back both times. Importantly, it uses weak references so that objects which are no longer in use are cleared from the intern table and it doesn't grow in size indefinitely.
One of the difficulties people have had with understanding the factory-pool from the notes example is that it's not generally necessary to make it work. Aside from the initial loading of the database, all the activity is driven from the front-end and so displaying a single view isn't strictly reliant on the signals firing correctly. If you replace the code to retrieve an object from the pool with a plain object instantiation, the default QML front-end for the demo would still work the same, albeit more wastefully.
To see the pool doing something useful, try the "notes-dual" front-end (by specifying it on the command line), which I've come to think of as the most interesting of the demos. It displays two front-ends simultaneously backed by the same data model. Changes in one are immediately reflected in the other. This works because when each front-end retrieves the list of notes they both obtains the same ObjRef from the pool for each Note as each other. Hence, when a change is made in one front-end, and a change signal is fired, both the front-ends receive it and remain in sync.
Without the pool, the onus would be on the application code to keep track of the objects in some other fashion. For example, if each read of the notes property created a new set of objects every time it was accessed then many more objects would need to have signals fired on them in order to ensure that every piece of active QML had received notice of the event. Each front-end would still be backed by the same data, methods and properties would still have access to the same set of Note values, but their objects would be distinct from the perspective of signalling and, unless special care was taken, the front-ends wouldn't update correctly.
I was messing with ghci when I tried to implement a YCombinator as followingλ: \f -> (\x -> f(x x)) (\x -> f(x x)) Occurs check: cannot construct the infinite type: t1 = t1 -> t0
I am not sure why it isn't working? Is it because Haskell is a kind of strongly normalized typed lambda calculi and thus self-application is forbidden (but general r?!)
If anyone can elaborate on what is the issue here, it would be greatly welcomed.
Also, bonus points for implementing a correct Y combinator in Haskell! :)submitted by ahar0n
[link] [6 comments]
Stack remembers things for you so that you don't have to. Here's an example.
I had a trivial change that I wanted to try out adding to http-client. Here are the steps I went through:
- git pull
- stack test # yep, project builds and passes test suites
- <<make change>>
- stack test # yep, it still builds and passes test suites
- git add <<my changes>>
- git commit -m <<explain my changes>>
- git push forked-origin
- <<submit PR on github>>
Notice the things I didn't have to remember to do:
- create a sandbox
- install dependencies
- test the other packages in the repo against my changes to http-client
Stack lowers the barrier to open source contributions. If your open source project comes with a stack.yaml, then it is easy for contributors to jump right in and get the real work done, rather than fiddling with package management. Just instruct contributors to download stack and stack test.
Something which is perhaps less obvious at first is how to get ghci to work with your stack-ified project. It's quite simple once you know:$ stack ghci Configuring GHCi with the following projects: http-client-openssl, http-client-tls, http-client, http-conduit GHCi, version 7.8.4: ... Prelude> :load Network.HTTP.Client Network.HTTP.Client> submitted by drb226
[link] [102 comments]
As vaguely promised before, another update on what I've been working on for the past couple of years:
Specifically, it is in the same space as Gqrx, SDR#, HDSDR, etc.: a program which runs on your computer (as opposed to embedded in a standalone radio) and uses a peripheral device (rtl-sdr, HackRF, USRP, etc.) for the RF interface. Given such a device, it can be used to listen to or otherwise decode a variety of radio transmissions (including the AM and FM broadcast bands everyone knows, but also shortwave, amateur radio, two-way radios, certain kinds of telemetry including aircraft positions, and more as I get around to it).
ShinySDR is basically my “I want my own one of these” project (the UI still shows signs of “I’ll just do what Gqrx did for now”), but it does have some unique features. I'll just quote myself from the README:
I (Kevin Reid) created ShinySDR out of dissatisfaction with the user interface of other SDR applications that were available to me. The overall goal is to make, not necessarily the most capable or efficient SDR application, but rather one which is, shall we say, not clunky.
Here’s some reasons for you to use ShinySDR:
Remote operation via browser-based UI: The receiver can be listened to and remotely controlled over a LAN or the Internet, as well as from the same machine the actual hardware is connected to. Required network bandwidth: 3 Mb/s to 8 Mb/s, depending on settings.
Phone/tablet compatible (though not pretty yet). Internet access is not required for local or LAN operation.
Persistent waterfall display: You can zoom, pan, and retune without losing any of the displayed history, whereas many other programs will discard anything which is temporarily offscreen, or the whole thing if the window is resized. If you zoom in to get a look at one signal, you can zoom out again.
Frequency database: Jump to favorite stations; catalog signals you hear; import published tables of band, channel, and station info; take notes. (Note: Saving changes to disk is not yet well-tested.)
Map: Plot station locations from the frequency database, position data from APRS and ADS-B, and mark your own location on the map. (Caveat: No basemap, i.e. streets and borders, is currently present.)
- Audio: AM, FM, WFM, SSB, CW.
- Other: APRS, Mode S/ADS-B, VOR.
If you’re a developer, here’s why you should consider working on ShinySDR (or: here’s why I wrote my own rather than contributing to another application):
All server code is Python, and has no mandatory build or install step.
Plugin system allows adding support for new modes (types of modulation) and hardware devices.
Demodulators prototyped in GNU Radio Companion can be turned into plugins with very little additional code. Control UI can be automatically generated or customized and is based on a generic networking layer.
On the other hand, you may find that the shiny thing is lacking substance: if you’re looking for functional features, we do not have the most modes, the best filters, or the lowest CPU usage. Many features are half-implemented (though I try not to have things that blatantly don’t work). There’s probably lots of code that will make a real DSP expert cringe.
Now that I've finally written this introduction post, I hope to get around to further posts related to the project.
At the moment, I'm working on adding the ability to transmit (given appropriate hardware), and secondarily improving the frequency database subsystem (particularly to have a useful collection of built-in databases and allow you to pick which ones you want to see).
Side note: ShinySDR may hold the current record for most popular program I've written by myself; at least, it's got 106 stars on GitHub. (Speaking of which: ShinySDR doesn't have a page anywhere on my own web site. Need to fix that — probably starting with a general topics/radio. Eventually I hope to have a publicly accessible demo instance, but there’s a few things I want to do to make it more multiuser and robust first.)
In my opinion I think this is a better proposal for fixing fail, "fail _ = mzero". Pros: Less work MonadPlus is already adopted in the mainstream. Follows the same laws as suggested in the MFP because of this MonadPlus law: mzero >>= f = mzero Almost all monads with sensible fail implementations are instances of MonadPlus and set fail _ = mzero Cons: Some monads use the string argument for error reporting. STM is a monad with a MonadPlus instance but errors out on fail. (I believe this is a mistake but i cant find any literature on it.)submitted by darkroom--
[link] [5 comments]
I didn't take any course on compiler design at the University because my interests lay elsewhere, but now I'd like to be able to design DSLs, create plugins for editors, etc...
In particular, I'm interested in statically typed functional languages such as Haskell or Idris.
There are many books on Compiler Design (e.g. Engineering a Compiler) but I'm afraid they only focus on imperative/OOP languages.
What should I read/study?submitted by Kiuhnm
[link] [22 comments]
I ran 'cabal update' on a device with ram less than 512MB, and it crashed because the machine ran out of memory. How am i supposed to install stuff with cabal now? Can i transfer it from another machine?submitted by Distort3d
[link] [13 comments]