Search code examples
haskellhaskell-turtle

Turtle in haskell: How to switch to a directory obtained from inshell command?


I am learning Haskell and I am having a hard time performing the following task.

I have a script that gives me a directory. I would like to call that script, get the directory and then use the "cd" in Turtle to change the directory. Below is the code that I currently wrote but it fail to compile.

It says lineToText targetDirLine expects Line and not Maybe Line . I do not understand why fold (inshell "getSomeDir" empty) Fold.head produces Maybe (Maybe Line).

Even after fixing this error I get other issue using cd.

#!/usr/bin/env stack
-- stack --resolver lts-10.2 script

{-# LANGUAGE OverloadedStrings #-}

import qualified Control.Foldl as Fold                         
import Turtle              

getDirAndCd :: MonadIO io => io ()
getDirAndCd = cd fp
              where (Just targetDirLine) = fold (inshell "getSomeDir" empty) Fold.head
                    fp = fromText (lineToText targetDirLine)

What am I missing?


Solution

  • If you inspect the type of the fold expression with GHCi:

    λ> :t fold (inshell "getSomeDir" empty) Fold.head
    fold (inshell "getSomeDir" empty) Fold.head
      :: MonadIO io => io (Maybe Line)
    

    you'll see that it's not a Maybe Line, it's an io (Maybe Line). When you write, in your where clause:

    where Just targetDirLine = fold (inshell "getSomeDir" empty) Fold.head
    

    it tries to match the Maybe on the left with the io on the right, and so io (Maybe Line) unifies with Maybe (Maybe Line), and that's why you get the type error you do.

    You'll want to process the fold expression in a <- line in the do-block:

    getDirAndCd :: (MonadIO io) => io ()
    getDirAndCd = do
      Just targetDirLine <- fold (inshell "getSomeDir" empty) Fold.head
      let fp = fromText (lineToText targetDirLine)
      cd fp
    

    This will result in another error, Could not deduce (MonadFail io). The issue here is that Just targetDirLine <- ... represents a pattern that could fail, if fold returns a Nothing. If you add a MonadFail io constraint, it type checks:

    {-# LANGUAGE OverloadedStrings #-}
    
    import qualified Control.Foldl as Fold
    import Turtle
    
    getDirAndCd :: (MonadFail io, MonadIO io) => io ()
    getDirAndCd = do
      Just targetDirLine <- fold (inshell "getSomeDir" empty) Fold.head
      let fp = fromText (lineToText targetDirLine)
      cd fp