How to convert Haskell System.Directory getHomeDirectory to a regular String?

StackOverflow https://stackoverflow.com/questions/15364779

  •  23-03-2022
  •  | 
  •  

Pregunta

I'm a Haskell noob and, at the moment, only use it to configure xmonad.

I want to put my config into a git repo, for that I want to not have to hardcode my home dir to grab my icons.

I checked out http://www.haskell.org/haskellwiki/How_to_get_rid_of_IO but I am just too ignorant to understand it.

hd h = h =<< getHomeDirectory

getIcon::String -> String
getIcon out = ( "^i("++hd++".xmonad/dzen2/"++out )

Is this actually possible? If so, how? I don't want to operate on the directory, I just want the path, as a String and it's killing me.

Error is:

Couldn't match expected type `[Char]'
            with actual type `(FilePath -> IO b0) -> IO b0'
In the first argument of `(++)', namely `hd'
In the second argument of `(++)', namely
  `hd ++ ".xmonad/dzen2/" ++ out'
In the expression: ("^i(" ++ hd ++ ".xmonad/dzen2/" ++ out)

Looks to me like the IO monad was not removed at all.

Update: Alright. I'll learn how to adapt to IOs rules, until then I'll keep things hardcoded and clone the config file with a script that will replace the appropriate bits.

¿Fue útil?

Solución

Your getIcon has the wrong type, since getHomeDirectory does IO:

getIcon :: String -> IO String
getIcon out = do
     hd <- getHomeDirectory
     return $ "^i(" ++ hd ++ ".xmonad/dzen2/" ++ out

Remember that Haskell distinguishes code that has side effects -- such as reading your hard disk -- via the type IO.

So the caller would be in IO too:

main = do
    s <- getIcon "foo"
    .. now you have a regular string 's' ...

Otros consejos

Can you change the code at the point where you're calling getIcon?

If you can get the home directory before calling it, you could do

getIcon :: String -> String -> String
getIcon out hd = ( "^i("++hd++".xmonad/dzen2/"++out )

and then wherever you're calling from (assuming it is in IO as well)

someIOFunction = do
    things
    ....
    hd <- getHomeDirectory
    getIcon out hd

Just to point out a last resort if nothing else works, there is unsafePerformIO, but I've never actually had to use it (and I feel like it is generally frowned upon), so I can't help you too much there.

You can "break out" of other monads, but you can't break out of the IO monad. Something like this in your Xmonad configuration file is probably what you want:

getIcon::String -> String
getIcon out = ( "^i("++hd++".xmonad/dzen2/"++out )

main =
   h <- getHomeDirectory
   let myIcon = getIcon "Firefox"
   xmonad $ desktopConfig . . . -- use myIcon somewhere in this expression

It's possible to unwrap certain IO values at compile-time, using Template Haskell. One's home directory is a reasonable candidate, since it doesn't change particularly often. A Template Haskell solution for getting the home dir as a string at compile-time might be coded like this:

{-# LANGUAGE TemplateHaskell #-}
module HomeDirectory where

import Language.Haskell.TH
import System.Directory

myHome :: String
myHome = $(LitE . StringL `fmap` runIO getHomeDirectory)

Be warned that Template Haskell is a notoriously tricky beast, though. This example is simple enough, but it can easily end up complex and confusing.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top