In haskell-chart, how do you change the range of an axis? It seems like it has something to do with lenses and viewports, but as a beginner in Haskell, I'm having a hard time reading the API:
http://hackage.haskell.org/package/Chart-0.14/docs/Graphics-Rendering-Chart-Axis-Types.html
Thanks for any help!
I would definitely appreciate information on better ways of doing this or more information on how this works with lenses (I still don't really understand what this is doing).
I will have a go at it. This will be a cursory explanation, not just for keeping things simple but also because I am still getting the hang of lens as well. Before starting, note that you linked to the docs of Chart 0.14, a version earlier than the conversion of the API to lens. Here are the current docs that you should refer to instead. Now, to your snippet:
layout_y_axis . laxis_generate .~ scaledAxis def (0,1)
It is a function which is applied to your layout. In it, layout_y_axis
and laxis_generate
are lenses. Lenses are references; in this case, references to fields in data types. In simple use cases, lenses look a lot field labels, except that, unlike record labels, they are first class, and lots of interesting things can be done with them. In fact, lenses are functions which can be composed with (.)
; the composition, however, is done from left to right, opposite to usual Haskell practice. Thus in:
layout_y_axis . laxis_generate
layout_y_axis
is a reference to a field in the layout, laxis_generate
is a reference to a field in the axis (the "function that generates the axis data", according to the docs); composing them (in reverse/OO order) gives a reference to the generating function of the Y axis of the layout.
Beyond the lenses themselves, the other key part of your snippet is (.~)
, one of the many lens operators. It produces setter functions; that is, it takes a reference and a value and produces a function which sets the target of the reference. In your case, you get a function which makes scaledAxis def (0,1)
the generating function of the Y axis of the layout.
Now, if you check the documentation of Graphics.Rendering.Chart.Layout you will find not just the lenses, but also _layout_y_axis
and _laxis_generate
, which are fields of Layout
and LayoutAxis
respectively. They are the backing fields of the lenses; in fact the lenses can be, and in this case are, generated automatically from them. Given that the module exports the field labels, you could write your function without lenses, using just record syntax:
\lay -> lay
{ _layout_y_axis =
(\yax -> yax { _laxis_generate = scaledAxis def (0,1) })
$ _layout_y_axis lay
}
That, however, is just too ugly. lens can do a lot more than improve on nasty syntax; in any case, for libraries like Chart
, in which field manipulation is done all over the API, just replacing the usual nested record syntax with something neat and composable is a very good thing already.
Note: In the "Easy" module in Chart-1.9, use .=
instead of .~
:
import qualified Graphics.Rendering.Chart.Backend.Cairo as C
import qualified Graphics.Rendering.Chart.Easy as C
plotit outfile points =
C.toFile C.def outfile $ do
C.layout_y_axis . C.laxis_generate C..= C.scaledAxis C.def (0, 1)
C.plot (C.points "n_coffees" points) -- etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With