Re: [Haskell-cafe] Lifting IO actions into Applicatives
On 10/01/2013 07:58 AM, Michael Snoyman wrote: I'm wondering if anyone's run into this problem before, and if there's a common solution. In Yesod, we have applicative forms (based originally on formlets). These forms are instances of Applicative, but not of Monad. Let's consider a situation where we want to get some user input to fill out a blog post datatype, which includes the current time: data Blog = Blog Title UTCTime Contents myBlogForm :: Form Blog myBlogForm = Blog $ titleForm * something * contentsForm The question is: what goes in something? Its type has to be: something :: Form UTCTime Ideally, I'd call getCurrentTime. The question is: how do I lift that into a Form? Since Form is only an Applicative, not a Monad, I can't create a MonadIO instance. However, Form is in fact built on top of IO[1]. And it's possible to create a MonadTrans instance for Form, since it's entirely possible to lift actions from the underlying functor/monad into Form. So something can be written as: something = lift $ liftIO getCurrentTime Is it really necessary to have a type class to do this? You can always just introduce 'io :: IO a - Form a' to lift IO actions into a form. Then you just have: myBlogForm = Blog $ titleForm * io getCurrentTime * contentsForm In digestive-functors, we have the base monad in the type of the form itself, so we provide 'monadic' which goes from m (Form m a) - Form m a (hand-waving as there are actually other type constraints). You might get more power by following in those steps, and having the aforementioned 'io' function actually be: io :: IO (Form a) - Form a - ocharles signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Poll plea: State of GUI graphics libraries in Haskell
On 09/27/2013 04:32 AM, Conal Elliott wrote: I'm polling to see whether there are will and expertise to reboot graphics and GUIs work in Haskell. I miss working on functional graphics and GUIs in Haskell, as I've been blocked for several years (eight?) due to the absence of low-level foundation libraries having the following properties: * cross-platform, * easily buildable, * GHCi-friendly, and * OpenGL-compatible. The last several times I tried Gtk2hs, I was unable to compile it on my Mac. Years ago when I was able to compile, the GUIs looked and interacted like a Linux app, which made them awkward and upleasant to use. wxHaskell (whose API and visual appearance I prefered) has for years been incompatible with GHCi, in that the second time I open a top-level window, the host process (GHCi) dies abruptly. Since my GUI graphics programs are often one-liners, and I tend to experiment a lot, using a full compilation greatly thwarts my flow. For many years, I've thought that the situation would eventually improve, since I'm far from the only person who wants GUIs or graphics from Haskell. We are working on bindings to SDL 2 at the moment - https://github.com/Lemmih/hsSDL2. They are currently usable for most 'stock' work - drawing things, doing interaction, window management, etc. However, I'm afraid SDL bindings don't really solve what you want in terms of a GUI programming. SDL2 at least gives you a sane cross platform way to create a window with an OpenGL context, and to draw things using hardware acceleration. If you actually need widgets, then SDL probably won't help here. - ocharles signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Readable GHC 7.6.3 docs (Bootstrapped)
On 09/11/2013 03:45 PM, Twan van Laarhoven wrote: Why does every section have a title=1.2.3 foo on the outer div? In Firefox this shows up as a useless tooltip when moving the mouse over the text. That's the same with the official document. I think it's a feature of whatever tool is used to generate this documentation. The NixOS documentation has the same annoying behavior - http://nixos.org/nix/manual/#idp125008 - ocharles signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Lenses: Should I declare Getters?
On 09/10/2013 06:31 AM, Charlie Paul wrote: I've been looking through Edward Kmett's lens library, and I'm a bit befuddled about Getters. In my own code, why would I want to have something be a Getter instead of a plain function? As far as I can see, a plain function is simpler to use, and can be converted to a Getter with to if we want to use it as a Fold. Is there a situation where writing a Getter is superior than writing a function, then lifting it as needed? Generally, you don't provide 'Getter's because people can always lift a function with to - as you said. This is mentioned in the lens FAQ at: https://github.com/ekmett/lens/wiki/FAQ#wiki-using-getters - ocharles signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Identity of indiscernibles
On 08/08/2013 05:05 PM, Tom Ellis wrote: On Thu, Aug 08, 2013 at 03:38:41PM +0200, Jerzy Karczmarczuk wrote: One could simply implement IO as a free monad Interesting. I wonder how. See [1] for an explanation of free monads in general You're lacking a matching definition of [1] :) signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Wrapping all fields of a data type in e.g. Maybe
On 07/16/2013 09:57 PM, Michael Orlitzky wrote: I have a common pattern in my command-line programs; I start out with a configuration data type, which over-simplified looks like: data Cfg = Cfg { verbose :: Bool } Now, there's usually a default configuration, default :: Cfg default = Cfg False The user can override the defaults one of two ways, either via a config file, or from the command-line. If both are specified, the command-line takes precedence. The way I do this is with, data OptionalCfg = OptionalCfg { verbose :: Maybe Bool } And then I define I Monoid instance for OptionalCfg which lets me merge two ofthem. Once the two OptionalCfgs are merged, I merge *that* with the default Cfg. This all works great, except that when there's 20 or so options, I duplicate a ton of code in the definition of OptionalCfg. Is there some pre-existing solution that will let me take a Cfg and create a new type with Cfg's fields wrapped in Maybe? One option is to combine OptionalCfg with Cfg, by parameterizing Cfg. If you make data Cfg a = Cfg { cfgVerbose :: a Bool } Then you can choose between Cfg Identity and Cfg Maybe. Furthermore, these are different types, so you can still have a monoid over Cfg Maybe. There might be some lens magic that makes working with this easier too. For example, verbose :: Lens' (Cfg a) Bool verbose = cfgVerbose.traverse Or something to that effect (that actually compiles). - ollie signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Netwire bouncing ball
On 07/11/2013 05:52 PM, Just wrote: On 07/10/2013 11:44 PM, Ertugrul Söylemez wrote: A very simple way to do this is to use integralLim_ instead of integral_. It allows the ball itself to handle the bouncing. A less invasive way (i.e. you can add it to your example) is to use the (--) combinator: ball = integral_ 0 . integral_ 40 . (-9.8) aboveGround = require (= 0) bouncingBall = aboveGround . ball -- bouncingBall While this gives you a bouncing ball, the ball will not follow real physics. Once the ball hits the ground, it will just start over with its original velocity. integralLim_ is the correct solution. Thank you very much, this works as expected and is easy to understand. However a complete example of a bouncing ball would be super awesome since I have trouble to get it work with integralLim_. My first try was to use object_ from Control.Wire.Prefab.Move but got stuck very quickly. I think this would be a good addition to the quickstart tutorial. It would indeed be a fantastic addition - this is almost exactly what I was trying to do as my example netwire project. For a bit more support, I was trying to move a point from x=0 to x=10, and then back to x=0 - and so on. I got as far as getting to x=10, but then had no idea how to reverse the direction and make the whole thing cycle. I was told to read about ArrowLoop, but sadly I never got much further. - ollie signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANNOUNCE: module-management-0.9.3 - clean import lists, split and merge modules
On 07/01/2013 08:38 PM, David Fox wrote: Should I keep posting updates in this thread? Yes please, I'm interested in following this development! That, or get yourself a blog and let us all know where to point our RSS readers. - Ollie signature.asc Description: OpenPGP digital signature ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] [haskell.org Google Summer of Code] The Summer of Code is officially under way.
On 18 Jun 2013 15:34, Edward Kmett ekm...@gmail.com wrote: I'm looking forward to seeing what we can accomplish together this year! +1 - best of luck, students ! Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: Angel 0.4.2
On 13 Jun 2013 06:51, Michael Xavier mich...@michaelxavier.net wrote: I'm pleased to announce the release of Angel 0.4.2 and that I have officially taken over maintainership of this project. Thanks to Jamie Turner for starting such a great project and allowing me to take over this project. Hi Michael, great to hear Angel is still active! Its a great project, so I wish you good luck as the new maintainer. Thanks for your work! - ocharles ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] What symbol / atom/ interned-string package is currently preferred?
On 05/09/2013 12:56 PM, Johannes Waldmann wrote: http://hackage.haskell.org/package/intern what does this package do? OK, I can read efficient hash consing but what does it mean exactly? and how would I actually use it? I can't tell you what it does, but there is an example - https://github.com/ekmett/intern/blob/master/examples/Term.hs ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: rematch, an library for composable assertions with human readable failure messages
On 04/16/2013 11:24 AM, Tom Ellis wrote: On Tue, Apr 16, 2013 at 10:17:48AM +0100, Tom Crayford wrote: I kept on running into this thing where I was calling error in quickcheck to get good error messages about the things I was comparing. In Java land, this stuff is handled by Hamcrest: a library for composable assertions with good error messages. This library is basically a port of hamcrest's core api, but I've been very pleased with how it turned out. [...] Let me know if you have any feedback/thoughts I've been feeling the need for something like this for some time. I'll check it out. Thanks! Tom This is beyond sorely needed. I can't wait to plug this into some of my own projects! Thanks! - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Monad fold
On 04/16/2013 01:47 PM, Lyndon Maydwell wrote: You could do: runKleisli . mconcat . map Kleisli :: Monoid (Kleisli m a b) = [a - m b] - a - m b Would that work for you? I can't find an instance for Monoid (Kleisli m a b) in `base`, so presumably the author would also have to write this instance? If so - would that really be any different to using that fold? - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Vector Fabrics is hiring!
On 04/08/2013 06:30 AM, Stefan Holdermans wrote: * Your friends and colleagues describe you as a rockstar programmer; your programming ability is way above average; Good luck hiring smart people. ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Fwd: Now Accepting Applications for Mentoring Organizations for GSoC 2013
On 03/18/2013 08:49 PM, Johan Tibell wrote: [bcc: hask...@haskell.org mailto:hask...@haskell.org] We should make sure that we apply for Google Summer of Code this year as well. It's been very successful in the previous year, where we have gotten several projects funded every year. Definitely - plus it means I get to meet other Haskell'ers at the mentor summit ;) - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] ANN: lazy-csv - the fastest and most space-efficient parser for CSV
On 02/25/2013 10:47 AM, Malcolm Wallace wrote: There are lots of Haskell CSV parsers out there. Most have poor error-reporting, and do not scale to large inputs. I am pleased to announce an industrial-strength library that is robust, fast, space-efficient, lazy, and scales to gigantic inputs with no loss of performance. http://code.haskell.org/lazy-csv/ Downloads from Hackage: http://hackage.haskell.org/package/lazy-csv This library has been in industrial use for several years now, but this is the first public release. No doubt the API is not as general as it could be, but it already serves many purposes very well. I'm happy to receive bug reports and suggestions for improvements. Regards, Malcolm Obvious question: How does this compare to cassava? Especially cassava's Data.CSV.Incremental module? I specifically ask because you mention that it's It is lazier, faster, more space-efficient, and more flexible in its treatment of errors, than any other extant Haskell CSV library on Hackage but there is no mention of cassava in the website. - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] JSON querying
On 02/17/2013 03:01 PM, Sergey Mironov wrote: Hi folks. Hackage contains several JSON packages but as far as I see, they all provide 'static' conversion from JSON format to Haskell data type. Is there a method of converting object containing optional filed 'a' to for example Maybe a. Assuming you have some sort of 'path' to the key in question, aeson-lens might be exactly what you want: http://hackage.haskell.org/package/aeson-lens I use aeson-lens to turn a list of strings of the form [foo, bar, baz] into a query into first the 'foo' object, then the 'bar' object, then the 'baz' object, using the following: pathToLens :: Functor f = [T.Text] - (Maybe Value - f (Maybe Value)) - Maybe Value - f (Maybe Value) pathToLens [p] = key p pathToLens ps = let ps' = filter (not . T.null) ps in key (head ps') . (foldl (.) id . map pathElem $ tail ps') where pathElem p = maybe (key p) nth (readMay $ T.unpack p) So as you can see - I turn any arbitrary path into a query into JSON, with a chance of failure. Hope this helps! - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Optimizing performance problems with Aeson rendering large Text arrays
Hello, In summary, i'm working on an application that responds to a users query, a sequence index, with the union of a list of UUIDs that have changed since that same sequence index, split into 6 sections. I wish to respond to these queries via JSON to provide an easy to use web service, and for the most part, what I have works. The problem I am having is that profiling seems to show that the majority of the time spent in my application is encoding this to JSON, and also that the application is only 60% productive with 40% allocations happening in Data.Aeson.encode (and friends). Here's an overview of what I'm doing, the full code can be found at the end of this email. I am storing my data in memory as an IntMap, from sequence index to a changeset: IntMap ChangeSet Where a ChangeSet is essentially a tuple of HashSet's of UUIDs: data ChangeSet = ChangeSet { artistChanges :: !(HashSet MBID) , labelChanges :: !(HashSet MBID) , recordingChanges :: !(HashSet MBID) , releaseChanges :: !(HashSet MBID) , releaseGroupChanges :: !(HashSet MBID) , workChanges :: !(HashSet MBID) } deriving (Generic) The MBID newtype is just a newtype around Text, but you can only create MBIDs by parsing a UUID fromString - just to enforce a bit more correctness, but without the cost of having to serialize the UUID to JSON. When I query, I splitLookup on the IntMap to get the requested change set by sequence index, and all future changesets. I union all of these, and then render the response back to the client: let (_, !cs, !futureCs) = IntMap.splitLookup csId changeSets writeLBS $ encode $ mconcat $ catMaybes $ map Just (IntMap.elems futureCs) ++ [ cs ] None of this shows up in profiling however, and here's what I see: Thu Jan 31 17:03 2013 Time and Allocation Profiling Report (Final) Main +RTS -p -RTS total time =4.75 secs (4748 ticks @ 1000 us, 1 processor) total alloc = 4,329,582,160 bytes (excludes profiling overheads) COST CENTRE MODULE %time %alloc encode Data.Aeson.Encode 23.5 17.4 string Data.Aeson.Encode 18.5 35.1 break Data.Aeson.Encode 17.5 2.3 mconcat Main15.1 9.7 fromValue/Array Data.Aeson.Encode9.2 14.8 toJSON Main 5.7 9.0 send.loop Snap.Internal.Http.Server.HttpPort 3.0 0.0 mapIter Snap.Iteratee2.1 2.3 parseJSON Main 1.7 3.0 writeLBSSnap.Internal.Types 1.1 4.9 COST CENTRE MODULEno. entries %time %alloc %time %alloc MAINMAIN 21600.00.0 100.0 100.0 main Main 43300.00.0 100.0 100.0 main.sinceMain 106310.00.075.3 78.7 encode Data.Aeson.Encode 13910 23.5 17.475.3 78.7 fromValue/ObjectData.Aeson.Encode1395 2540.0 0.046.0 52.2 fromValue/ArrayData.Aeson.Encode1420 7577.9 12.036.4 25.7 fromValue/String Data.Aeson.Encode1422 30890950.7 0.028.5 13.7 string Data.Aeson.Encode1423 3089095 10.2 11.427.8 13.7 break Data.Aeson.Encode1425 3089095 17.5 2.317.52.3 string Data.Aeson.Encode1396 8848.3 23.7 9.6 26.6 fromValue/Array Data.Aeson.Encode 142101.3 2.9 1.32.9 fromValue/String Data.Aeson.Encode 142400.0 0.0 0.00.0 break Data.Aeson.Encode1397 8840.0 0.0 0.00.0 toJSON Main 1393 1275.7 9.0 5.79.0 Unless I'm reading this incorrectly, this shows that 75% of the time is spent in encode, along with almost 80% of my allocations. While the performance of my application is actually satisfactory (I respond in around 0.04s), I'd still like to do better - if only for the practical experience of learning how to optimize. Any ideas what I can do about this? I feel like I might get better performance if fromValue/Array new that I had a vector of Text values, and they could just be intercalated with , - but I have no idea how the internals of Text works so this might really perform the same as the fold that is currently used. I am compiling for benchmarking purposes with: ghc -Wall -fno-warn-orphans -Werror -O2 -rtsopts \ -hide-package hashable-1.2.0.5 Main.hs And I
Re: [Haskell-cafe] Optimizing performance problems with Aeson rendering large Text arrays
Urgh, the formatting got totally destroyed in sending, I think. If so, here's a paste of my email as I intended it to be sent: http://hpaste.org/81648 Sorry about that! - Ocharles ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Optimizing performance problems with Aeson rendering large Text arrays
On 02/01/2013 01:44 PM, Bas van Dijk wrote: If I make a special case for text based UUIDs in aeson: data Value = ... | UUID Text | ... Data.Aeson.Encode.fromValue (UUID s) = singleton '' fromText s singleton '' Then encoding time improves by 20%. So a big part of the time is spent encoding the UUID strings. Bas This might work, but it doesn't seem to be something I could actually distribute, unless I shipped modified Aeson source code... the String fromValue pattern seems to do a lot of escaping which won't apply to my UUIDs, but I'm not sure what the right solution to that is. - Ocharles ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Looking for advice on modelling a 3-way merge (with varying strategies)
Hello, I'm currently trying to implement a way 3 way merge, that is dispatched on type. Essentially, I want to be able to say I have this large data type which is a product of various other types - here's how to merge it. By 3 way merge, I mean that I have a 'new' value, a 'current' value and a 'common ancestor' value. So far I have: import Control.Applicative import Data.Monoid newtype Merge a = Merge { runMerge :: Either [String] a } instance Functor Merge where fmap f a = Merge $ fmap f $ runMerge a instance Applicative Merge where pure = Merge . Right (Merge (Right f)) * (Merge (Right a)) = Merge $ Right $ f a (Merge (Right _)) * (Merge (Left c)) = Merge $ Left $ c (Merge (Left c)) * (Merge (Left c')) = Merge $ Left $ c c' (Merge (Left c)) * _ = Merge $ Left $ c ok :: a - Merge a ok = pure failMerge :: String - Merge a failMerge x = Merge $ Left [x] So far so good, so I can add my first merge strategy: mergeEq :: Eq a = String - a - a - a - Merge a mergeEq lbl new current ancestor | current == new = ok new | current == ancestor = ok new | new == ancestor = ok current | otherwise = failMerge lbl And we can make use of this to build more complicated merge strategies: data Person = Person { name :: String, surname :: String } deriving (Show) mergeEqOn :: Eq a = (b - a) - String - b - b - b - Merge a mergeEqOn l lbl n c a = mergeEq lbl (l n) (l c) (l a) mergePerson :: Person - Person - Person - Merge Person mergePerson new current ancestor = Person $ mergeEqOn name name new current ancestor * mergeEqOn surname surname new current ancestor runMerge $ mergePerson Person { name=Steve, surname=Bobman } Person { name=Joe, surname=Obman } Person { name=oe,surname=Obman } Left [name] runMerge $ mergePerson Person { name=Steve, surname=Bobman } Person { name=Joe, surname=Obman } Person { name=Joe, surname=Obman } Right (Person {name = Steve, surname = Bobman}) Everything does exactly what I want, but it doesn't smell as good as I expect from Haskell. Firstly, all my primitives or operations take 3 arguments that have to be threaded in a very specific pattern. If I accidently call mergeEq ancestor current new, I've ran things in the wrong order and I'm Gonna Have a Bad Time. So that's no good. Secondly, is there a better way of labelled parts of a merge? I suspect not, as at some point I need a human readable display, but I did wonder about using a lens as a label, and then mapping lenses to human readable names later -- but that will require a bit of newtyping in order to provide the Eq instance, or maybe i make a HumanLens type which is a lens and a name?). Finally, I'm currently a little obsessed with the idea of building applicatives from the composition or product of other applicatives - can I use this trick here? I initially used 'Compose ((,) [String]) Maybe a' but this means it's possible to have ([Conflicts], Just But success!), which is nonsense. I'd love to hear your thoughts! - ocharles ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Is it possible to create an instance of MonadBaseControl for PropertyM (QuickCheck)/continuations?
Hi I'm working on a little experiment at the moment: using monadic QuickCheck to test the integration of my code and the database. I see some of my functions having properties like - given a database in this state, selectAll should return all rows, and so on. My initial attempt has worked nicely, and now I'm trying to test some more complicated properties, but I'm hitting a problem with overlapping primary keys, and this is because I'm not correctly cleaning up after each check. The simplest solution to this is to bracket property itself, and for that I turned to Control.Monad.Trans.Control in lifted-base, but I am struggling to actually write an instance for MonadBaseContol IO (PropertyM IO). It seems that PropertyM is a continuation monad transformer: newtype PropertyM m a = MkPropertyM { unPropertyM :: (a - Gen (m Property)) - Gen (m Property) } Given this, is it possible to even write an instance of MonadBaseControl? From the lifted-base documentation, it explicitly calls out ConT as *not* having instances, so I wonder if it can't be done. Sadly, I also lack intuition as to what MonadBaseControl really means - I think it means 'capture the state of this monad, so later we can go back to exactly the same state (or sequence of actions?)', but this is very flakey. So... is this possible, and if so how can I move forward? Thanks for any help or advice! - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Idiomatic Aeson
Hi, I'm not sure it directly helps, but I had a bit of trouble getting off the ground with Aeson too. Here's some of my code using Aeson, maybe there will be something in here that helps you? https://github.com/ocharles/BookBrainz/blob/master/src/BookBrainz/Search.hs - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] indentation blues
Paul Koerbitz paul.koerb...@gmail.com writes: Hello, TL;DR: If you have some time try emacs, the viper / vimpulse plugins are pretty good and the editor is awesome in general. Haskell indentation is good. Not to go too off topic, but I'm not sure people are aware there's another Vim emulation system called Evil: http://gitorious.org/evil/pages/Home Started March this year apparently, and should be the successor to vimpulse. - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Using QuickCheck to test against a database
Hi! I've just spent my evening trying to use QuickCheck properties to test the database accessing functions in a project I'm working on at the moment. I've got some stuff that's working, and I wanted to see what the cafe thought about it, and if anyone had any suggestions or critiscm that can help me make it even better! I got here by trying to figure out how I want to test my application. At first I saw 2 options: use a test database and assume it exists, or have each test setup the data it needs. I like the latter more, because it minimises dependence on the outside world. So I got down to writing some HUnit tests and then had a wave of inspiration... what if I didn't even define the data, but let QuickCheck do that for me? Here's what I came up with - note the code here is somewhat paraphrased for brevity: data DBState a = DBState { initDb :: IO () , entity :: a } This type stores an action that initialises the database with enough data such that it contains whatever 'entity' is. For example, 'DBState Person' knows how to insert a specific person into the database, and carries along an in-memory log. Continuing with the Person idea, I then went and made an Arbitrary instance for DBState Person: instance Arbitrary (DBState Person) where arbitrary = do l - Person $ arbitrary `suchThat` (not . null) return $ DBState (void $ insertPerson l) p where insertPerson p = doSql INSERT INTO person (name) VALUES (?) [ personName p l ] Then it's just a case of defining some properties: prop_allPersons = monadicIO $ do dbState - pick arbitrary :: Gen [Person] fetched - run $ initDb `mapM` dbState findAllPersons assert $ fetched `eqSet` (entity `map` dbState) where eqSet a b = all (`elem` a) b all (`elem` b) a Voila! For any database, 'findAllPersons' returns all persons. Now... onto my own critiscisms with this. Firstly, way too much boiler plate. You have to remember to apply all of the states of your arbitrary instances, which is a pain, and guaranteed to be missed. At first I thought DBState looks like it's basically a writer/IO monad, but then I couldn't actually figure out the monoid to write to. In this case it's just [Person], but in more complex property we might need 2 types of entities (for example, to ensure a join worked correctly) which makes this list heterogenous. Secondly, the initDb action is sensitive to the order actions are sequenced. Presumably, I want to test against a database that's as accurate to production as possible, which means I want foreign keys enabled. If things are initialized in the wrong order, it violates the foreign key constraint, and it's a burden if people have to determine the correct order. One solution here is to make use of deferrable constraints, but support for these is flakey at best (and it doesn't work for bracketting tests with BEGIN; ROLLBACK). These problems just make the properties harder, but they don't seem to impede the quality or usefulness of the tests. I really want to make this work, because the idea of it is quite beautiful to me. Though that might be partly because this is my first time really using QuickCheck, so I'm getting the oh wow this is swt effect at the same time ;) Would love to hear peoples thoughts! - Ollie ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Please review my Xapian foreign function interface
Hello! I've finally came up with some motivation for a project to get my feet wet using Haskell, and for this little pet project I need an interface to Xapian. After reading various documents on FFI in general, I've got a brief working implementation, and I'm now looking for how to better structure the public API. First, a quick bit of background if you're not familiar with Xapian. Xapian is a search engine, and provides a C++ API. You store documents in a database (handled by Xapian), and index documents by adding terms to them. Xapian provides stemming algorithms to help generate these terms from other data. Xapian also has an interface to queries (through a Xapian::Enquire object), and also a query parser to allow for natural language queries to be parsed and ran. For more information, you can check out the API at [1] - it's fairly small. As Xapian is C++, it seems my best option is to create my own simple C wrapper, which also lets me tailor my FFI to be easy to use from Haskell. You can see my C api on Github [2] - for now it's very stripped down; I've been wrapping stuff on a need-to-use basis. * * * Currently what I have is functional (in the sense that it works), but it's extremely tied to I/O and very little of the code is pure. For example, to create and index a document, you need to do something along the lines of: do document - newDocument setDocumentData document Document data addPosting document search_term 1 addDocument database document (Assuming you already have an open database handle). How horrible imperative this all looks! :-) A document *feels* like it should be quite pure, however retrieving properties of a document performs I/O. For example, I'd like to have something like: data Document = Document { data :: String, postings :: [String] } do document - getDocument database 123 -- Get doc #123 and have `document` refer to a pure Document object. I'm still stuck in the IO monad a bit, but at least I can write pure functions to operate on `Document` values now. The problem I see with this, is that I believe I'd have to retrieve all parts of document in my `getDocument` function (include the data and all postings), and I can't benefit from being lazy here. From what I gather, all the methods on Xapian documents are lazy (such as getting the document data, and getting terms associated with documents), which would mean that my foreign imports would have to be `IO String`, for example. This tends to fairly quick cause the IO monad to propogate everywhere. * * * I think that's enough information to explain my current progress, and my concerns. It could well be that I'm overly worrying about everything being in the IO monad, but as I said - Haskell is new to me. All of my work is at [3], and I'd love any advice you have. Haddock documents have been exported to ocharles.org.uk [4]. Thanks for your time, Oliver Charles / ocharles -- [1]: http://xapian.org/docs/apidoc/html/annotated.html [2]: https://github.com/ocharles/Xapian-Haskell/blob/master/c/cxapian.h [3]: https://github.com/ocharles/Xapian-Haskell [4]: http://ocharles.org.uk/tmp/search-xapian/Search-Xapian.html ___ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe