Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any Haskell-land equivalent to the Ruby-land's Bundler et. al and, if not, how would a project so structured be contrived?

Tags:

Note to readers: Bear with me. I promise there's a question.


I have a problem to solve and think to myself "Oh, I'll do it in Ruby."

$ bundle gem problemsolver
      create  problemsolver/Gemfile
      create  problemsolver/Rakefile
      create  problemsolver/.gitignore
      create  problemsolver/problemsolver.gemspec
      create  problemsolver/lib/problemsolver.rb
      create  problemsolver/lib/problemsolver/version.rb
Initializating git repo in /tmp/harang/problemsolver

Remove the comment on s.add_development_dependency "rspec" in problemsolver/problemsolver.gemspec and then

$ bundle exec rspec --init
The --configure option no longer needs any arguments, so true was ignored.
  create   spec/spec_helper.rb
  create   .rspec

New tests go into spec/ and must be in files that end in _spec.rb. For instance, spec/version_spec.rb

describe 'Problemsolver' do
  it 'should be at version 0.0.1' do
    Problemsolver::VERSION.should == '0.0.1'
  end
end

To run specs--ignorning code-change runners like guard--is trivial:

$ bundle exec rspec
.

Finished in 0.00021 seconds
1 example, 0 failures

You can't see it, but the message is nicely color coded for quick "Did I screw up?" scanning? The things that are very good about this:

  • Setup was rapid, almost brainless (though figuring out which commands to invoke is not trivial).
  • Standardized layout of the source tree reduces the familiarization period with a new code-base, making collaboration more simple and reducing the lull time when picking up a project you've left for a bit.
  • A heavy reliance on tooling distributes best-practices through the community, roughly at the speed of new project creation.

Adding coverage tools, code watchers, linters, behavior test tools and others is no more difficult.


This stands unfavorably in contrast to the situation if one thinks, "Oh, I'll do it in Haskell."

$ mkdir problemsolver

$ cd problemsolver/

$ cabal init
Package name [default "problemsolver"]? 
Package version [default "0.1"]? 0.0.1
Please choose a license:
   1) GPL
   2) GPL-2
   3) GPL-3
   4) LGPL
   5) LGPL-2.1
   6) LGPL-3
 * 7) BSD3
   8) MIT
   9) PublicDomain
  10) AllRightsReserved
  11) OtherLicense
  12) Other (specify)
Your choice [default "BSD3"]? 
Author name? Brian L. Troutwine
Maintainer email [default "[email protected]"]? 
Project homepage/repo URL? 
Project synopsis? Solves a problem.
Project category:
   1) Codec
   2) Concurrency
   3) Control
   4) Data
   5) Database
   6) Development
   7) Distribution
   8) Game
   9) Graphics
  10) Language
  11) Math
  12) Network
  13) Sound
  14) System
  15) Testing
  16) Text
  17) Web
  18) Other (specify)
Your choice? ProblemSolver
ProblemSolver is not a valid choice.
Your choice? 18
Please specify? ProblemSolver
What does the package build:
   1) Library
   2) Executable
Your choice? 2
Generating LICENSE...
Generating Setup.hs...
Generating y.cabal...

You may want to edit the .cabal file and add a Description field.

"Great," you think, "I was so pestered I bet all the latest Haskell best-practices in software development are just waiting on my disk."

$ ls
LICENSE  problemsolver.cabal  Setup.hs

Allow me to summarize my feelings: :(

The generated cabal file doesn't even have a Main specified, much less instructions for setting up a rudimentary project. Still, okay. If you fart around for a bit trying to find the right search keywords you'll land on How to write a Haskell program which is okay except:

  • All of Haq source code gets thrown into the root directory.
  • The test code for Haq is only in Test.hs, is only QuickCheck and has no facility for continuing the project with split-file tests.
  • All of this has to be manually written or copied for each new project.

Checking Real World Haskell's Chapter 11 you'll find it doesn't even mention cabal and skirts the issue of project layout entirely. None of the resources that Don Stewart kindly answers with here are addressed in either of the aforementioned and, I'll note, Mr. Stewart doesn't explain how to use any of the tools referenced.

Note that the accepted answer in Haskell testing workflow references a project that's since moved on sufficiently so as not be a good answer but does say

As cabal test doesn't yet exist -- we have a student working on it for this year's summer of code! -- the best mechanism we have is to use cabal's user hook mechanism.

Hey, okay, the cabal documentation! The appropriate section does have examples, but they're awfully contrived but don't fail to give the impression that everyone is on their own and good luck to you.

Of course, there's always test-framework that seems to be nice but it example code doesn't offer anything beyond what's seen in the wiki and is non-scalable in the sense that as soon as my program grows in complexity I'm on the hook to develop ways of dividing up tests into manageable modules. I'm not even sure what's going on with HTF and agree with Mr. Volkov's assessment.

Mr. Jelvis' comment on the linked HTF question was of particular interest to me: the Haskell tool-chain suffers, very badly, from a tyranny of small decisions. I can't actually get down to the task at hand--solving my problem in Haskell--because I'm on the hook for getting my environment just right. Why this is bad:

  • It's wasted effort. Unless I'm writing a test tool, I will very, very rarely care about how my tests are slurped up, only from where.
  • It's difficult to learn. There seems to be no singular resource for setting up a project with testing baked in, and the various sources that do exist are sufficiently diverse as to be unhelpful.
  • It's difficult to reproduce. With so many moving pieces to arrange I'm bound to do it differently each time.
  • As a corollary, it's idiosyncratic. That means its difficult to collaborate and to pick up dormant projects.

This just plain stinks.

Maybe I'm wrong, though. Does there exist some poorly advertised tool or closely developed tools to do something similar to Bundler+Rspec in the Haskell space? If not, is there a poorly advertised canonical example of modern Haskell testing with all of Mr. Stewart's referenced goodies baked right in? The project created or demonstrated:

  • should by convention and tooling keep test code separate from application code in a well-defined manner (in Ruby-land, Rspec tests go in spec/, Cucumber features in features/),
  • should not require end-users to compile and install testing dependencies
  • should be easily reproducible, desirably in no more than 10 minutes and
  • should be standardized or have the hope of standardization.

Am I wrong in believing that there's nothing at all like this in Haskell-land?


Edit0: Please note, the Ruby language's community isn't the only applicable comparison. Paul R. is correct in identifying the strong current of configuration over convention. Other languages solve the problem of getting a scalable project structure off the ground in other ways:

  • C :: This language is venerable and so well-documented that you'll have trouble figuring out which well-documented approach to take. No tooling as such.
  • Java :: Configuration over convention: you're bound into it at the compiler level. Many tools and very well documented.
  • Scala :: Strong tool support.
  • Erlang :: Venerable and loosely documented if you know what you're looking for. Arguably configuration over convention if you're using rebar or are otherwise targeting the OTP.

Paul R.'s solution of using a custom template works great if, like C, there's sufficient documentation to compile such a thing. This still runs into issues that I attempted to identify explicitly in the post, but it's workable. Haskell's best offering--that I'm aware of--is "How to write a Haskell program" but falls short of being more than the equivalent of dumping a lone Cub Scout off in the woods with a flashlight and a flask of water.

Also, yes, Static Types are great and do solve many problems that would otherwise need explicit testing. If they were an end-all solution, or mostly sufficient, even, the snap-framework would not be so thoroughly tested. (Arguably "Copy snap-core." is an answer to my question.)

like image 907
troutwine Avatar asked Mar 12 '12 06:03

troutwine


2 Answers

There's currently no one single way to set up a testsuite. Hopefully, people will standardize on cabal test, which is out-of-the box. In fact, both HUnit and QuickCheck are also provided with the Haskell Platform, and so setting up tests doesn't require downloading any extra dependencies.

You're correct that an old accepted answer doesn't provide information on cabal test. I edited it, and now it does! You're also probably correct that the linked page on the Haskell wiki (also written before cabal test became available) doesn't provide information on current testing best practices. It's a wiki, and I encourage folks to edit it! Note that the page does, however, provide a link to another page that describes how one might structure a more complex Haskell project.

tldr; Use cabal test. I'm fond of test-framework, which you can integrate with cabal test should you so desire. Sorry that cabal test is sort of new and not all the resources we have (generally community editable) have been updated to point to it and describe how to use it. Updating lots of resources and creating tutorials is the job of a community. We should probably do a better job promoting lots of the new awesome tools introduced to the Haskell ecosystem in the last few years.

like image 126
sclv Avatar answered Dec 21 '22 16:12

sclv


There are many points here. First, there is a comparison of convention-over-configuration with explicit configuration. In the ruby land, the former is often prefered. In my experience, although it works great for do-a-{blog|social-thing|gem|library}-in-5-minutes-screencast and quick experiments, it has much less value in your real projects (more than 5 minutes) as init time gets quickly amortized. Also, there is a reason why tools provide configuration facilities : there are many different needs and usages. So my advice to your cabal-init problem is : make your own template file. Put stub for everything you need, with great comments, and use it whenever you need it.

Regarding tests, the landscape is quiet different between ruby and haskell. In ruby, one can write foo do { oh dear I am typing nonsense here } and there is no other way to catch this nonsense than actually running the code. So automated tests are absolutely required. In the haskell land however, there is a great static analysis of your code coupled with a very sane paradigm (purely functional non-strict), and after years of using it, I'm still surprised haw hard it is to write nonsense without being immediately caught by the compiler. I do ruby at work as well, and really, 90% of my tests are poor-man manual "static checks".

Still, there is room for wrong design or corner-case errors, that's why quickcheck exists. It will automatically (yes, really automatically) find corner-case errors and help you a lot find design errors. You can still write unit tests with one of existing packages if you need manual checks.

So my conclusion here is : don't be surprised to find shadow everywhere if you shade ruby light on the haskell land. Things are very different over here, and need to be experienced to grab power. That doesn't mean that everything is perfect, actually improving the toolchain is a commonly expressed wish. Just the points you raised are not really problematic, and really don't deserve some vocabulary you picked. Try first, judge after :)

like image 41
Paul R Avatar answered Dec 21 '22 15:12

Paul R