Writing Better
   Haskell
TL;DR:You can start off
  bad, and get better
Case Study: Kit
$ cabal install kit
Dependency manager for Obj-C code
Kit’s Origins

• 80% of the functionality needed produced
  in one (alcohol fueled) weekend
• Very little Haskell knowledge
• No internet access, no guidance.
It’s bad code.
How bad?

• IO everywhere
• Very little usage of the standard library, or
  any other libraries
• General ugliness
It could only get better.
Clearing out IO
Kit’s IO

• Producing Xcode projects - project files,
  config files
• Reading Specs - a configuration file for a
  Kit
Writing Files


createKitConfig :: Config -> IO ()
FS Actions
• data FSAction =
• FileCreate FilePath String |
• Symlink FilePath FilePath |
• InDir FilePath FSAction

• runAction :: FSAction -> IO ()
FSAction

createKitConfig :: Config -> FSAction


setupKitDirectory :: KitSpec ->
[FSAction]
Next: Cleaning IO

• Reader monad for specifying the
  Environment
• Writer monad for pure logging
• State monad for pure repository
  manipulation
Learning the
   StdLib
   [and hackage]
Unfolding
        Dependencies
• treeDependencies :: Kit -> Tree Kit
• totalDependencies :: Kit -> [Kit]

• Order is actually important.
Unfolding
        Dependencies

• Preserve order: lowest level deps first, keep
  dep order listing from Spec file


• No duplicates
Unfolding
        Dependencies


nub . concat . reverse . drop 1 . levels
Other learns:
• Globbing:
 • Shelling out to ruby, vs
 • the glob package
• CmdArgs:
 • Pattern match on arrays of args, vs
 • CmdArgs package
General Ugliness
    Tidying up imports
Prefix headers


• Used in [Obj-]C applications to provide
  global imports.
• Application-specific base library
Before
• import System.FilePath.Posix
• import System.Directory
• import Control.Applicative
• import Control.Monad

• In every damn file.
After
module Kit.Util(
 module Kit.Util,
 module Control.Applicative,
 module Control.Monad,
 module System.Directory,
 module System.FilePath.Posix
 ) where
After


• import Kit.Util
So, is it good yet?
No.
• Plug through WriterT for Logging
• Remove IO from the Monad stack
• When IO is gone, define invariants in
  quickcheck properties
• Learn how to specify those properties
  through types

Writing Better Haskell

Editor's Notes

  • #2 \n
  • #3 And getting better is easy\n
  • #4 What Kit is\nCombines multiple dependency into one static library\nMuch easy to manage in Xcode\nEasier to manage individual deps through a KitSpec than using VC sub-modules\n\n
  • #5 Explain what kit is\nUsed in mogeneration’s day-to-day app development. Crucial to version management of our apps and dependencies.\n
  • #6 Not the best environment for cultivating good code.\n
  • #7 \n
  • #8 what does bad mean, in a haskell sense\n
  • #9 This is a story of some the ways in which I’m making that happen.\nClearing out IO\nUsing other peoples code\nGeneral ugliness\n
  • #10 First step in clearing it out is identifying what sort of IO you’re doing\nWhy is IO on a function? \nWhy is this bad? Side effecting functions are difficult to decompose\n
  • #11 Why kit does: templates\n
  • #12 Implementation writes a file to a particular sub-dir, needed to maybe create the sub-dir, and write the file.\nThis could be (and was) split up into a function that produces the content, and a function that writes the content. But at some point you still need IO deep in your application.\n\n
  • #13 3 common file system ‘writes’\nLets me test content created, and where\nCleans up a lot of ‘create parent dirs, write file’ procedures\n
  • #14 Every file writing operation now produces an FSAction, top level functions can be IO-less, and just have many actions to run, and each action can be tested (quickchecked)\n
  • #15 next steps on clearing out IO, some of this as per tony, all are good for additional talks\n
  • #16 Pretty typical milestone when you’re learning any language, is getting to grips with the std lib. Kit was written in a vaccuum... so lots of opportunities to improve on what it did initially\n
  • #17 Dependencies form a tree\nCollapsing that tree in the wrong way caused bugs\n\n\n
  • #18 Children are needed in the list before the parent, so lowest levels deps should come first\nNo duplicates\nbig TODO: version resolution\n
  • #19 nub . reverse . tail . flatten\n
  • #20 \n
  • #21 \n
  • #22 \n
  • #23 I love applicatives, I love monads, and Kit does a *lot* of shell/FS access.\n\n\n
  • #24 \n
  • #25 Somewhat similar to package objects in scala (or a typical ruby project set up)\nUtil is probably a bad name\n\n
  • #26 \n
  • #27 Lots of things still to go, however the moral of the story (if there is one), is that it has been fairly easy to re-work parts of Kit to be better. Much easier than in other environments. Benefit of a very good type system...\n\n