Search code examples
haskelllibgit2

No instance for (Git.Libgit2.HasLgRepo IO) arising from a use of ‘lookupBlob’


I am trying to get list of all files in HEAD commit from a git repository using gitlib. Here is my code:

{-# LANGUAGE OverloadedStrings #-}

module GitOperations where

import Git
import Git.Libgit2 (lgFactory, LgRepo(..) )
import qualified Data.Text as T
import Control.Monad.Trans.Reader
import Control.Monad.Trans
import Data.ByteString.Char8 as BS

repositoryOptions :: RepositoryOptions
repositoryOptions = RepositoryOptions { repoPath = ".git"
                                , repoWorkingDir = Just "."
                                , repoIsBare = False
                                , repoAutoCreate = True}
  
main :: IO ()
main = do
  repo <- openRepository lgFactory repositoryOptions
  runReaderT listHEADFiles repo

listHEADFiles:: ReaderT LgRepo IO ()
listHEADFiles = do 
  ref <- resolveReference $ T.pack "HEAD"
  case ref of
    Nothing -> fail $ "Could not resolve reference named 'HEAD'"
    Just reference -> do
      obj <- lookupObject reference
      case obj of
        CommitObj commit -> do 
          objects <- listAllObjects (Just $ commitOid commit) (commitOid commit)
          lift $ mapM_ (\o -> case o of BlobObjOid boOid -> do blob <- lookupBlob boOid; bc <- blobContents blob; case bc of BlobString bs -> BS.putStrLn bs) objects
        _ -> fail $ "'HEAD' is not a commit object"

The error I'm getting is:

Main.hs:33:96: error:
    • Couldn't match kind ‘* -> *’ with ‘*’
      When matching types
        IO :: * -> *
        BlobContents :: (* -> *) -> *
      Expected type: IO (BlobContents m0)
        Actual type: BlobContents IO
    • In a stmt of a 'do' block: bc <- blobContents blob
      In the expression:
        do blob <- lookupBlob boOid
           bc <- blobContents blob
           case bc of { BlobString bs -> BS.putStrLn bs }
      In a case alternative:
          BlobObjOid boOid
            -> do blob <- lookupBlob boOid
                  bc <- blobContents blob
                  case bc of { BlobString bs -> BS.putStrLn bs }
   |
33 |           lift $ mapM_ (\o -> case o of BlobObjOid boOid -> do blob <- lookupBlob boOid; bc <- blobContents blob; case bc of BlobString bs -> BS.putStrLn bs) objects
   |                                                                                                ^^^^^^^^^^^^^^^^^
Failed, no modules loaded.

I do not understand how to go around this error message and how to adapt my code to make typechecker happy. Any tips are appreciated.

Note that git repository should contain at least one commit for the script to work.

UPD: Per @chi's recommendation, I have changed call to blobContents blob to be non-monadic, but now that relevant line looks like lift $ mapM_ (\o -> case o of BlobObjOid boOid -> do blob <- lookupBlob boOid; let bc = blobContents blob in case bc of BlobString bs -> BS.putStrLn bs) objects, another error comes up and I don't know how to fix it:

Main.hs:33:72: error:
    • No instance for (Git.Libgit2.HasLgRepo IO)
        arising from a use of ‘lookupBlob’
    • In a stmt of a 'do' block: blob <- lookupBlob boOid
      In the expression:
        do blob <- lookupBlob boOid
           let bc = ... in case bc of { BlobString bs -> BS.putStrLn bs }
      In a case alternative:
          BlobObjOid boOid
            -> do blob <- lookupBlob boOid
                  let ... in case bc of { BlobString bs -> ... }
   |
33 |           lift $ mapM_ (\o -> case o of BlobObjOid boOid -> do blob <- lookupBlob boOid; let bc = blobContents blob in case bc of BlobString bs -> BS.putStrLn bs) objects
   |                                                                        ^^^^^^^^^^^^^^^^
Failed, no modules loaded.

I am pretty new to gitlib and it is overwhelming for me now that I try to make simple things work with git in haskell. Does anyone have any idea how to make this code compile successfully?


Solution

  • If I found the correct documentation page, blobContents looks like a non-monadic function, so you can't use bc <- blobContents blob. Use instead let bc = blobContents blob.

    This is consistent with the error message, which states that the actual type is not wrapped under IO.

      Expected type: IO (BlobContents m0)
        Actual type: BlobContents IO
    

    Further, you use lift too early:

    lift $ mapM_ (\o -> case o of 
       BlobObjOid boOid -> do
          blob <- lookupBlob boOid
          let bc = blobContents blob
          case bc of BlobString bs -> BS.putStrLn bs
       ) objects
    

    Inside the lift you are constrained to use IO actions, only, and can not use the ones which require ReaderT LgRepo IO. To solve this issue, simply move the lift inwards:

    mapM_ (\o -> case o of 
       BlobObjOid boOid -> do
          blob <- lookupBlob boOid
          let bc = blobContents blob
          case bc of BlobString bs -> lift $ BS.putStrLn bs
       ) objects