I am trying to use a virtual shell using Haskell. I mean, what I mean is to create a program that when executed from a terminal window turns you into an environment that allows you to execute a subset of familiar commands (like cd and mkdir ) among others.
The file system will be completely virtual, since it does not touch anything on the disk.
My initial sketch (below) seems awkward, so much so that drawing up the MWE was a drag and drop, Iām very unaccustomed to presenting data structures and would like some guidance on how to turn this experience around. Any help program with accessible source code will be useful, like any advice or line of thought.
I wrote the following
import qualified Data.Map as Map import qualified Data.Either as Either type Name = String type Content = String type Error = String data DiskEntry = File Name Content | Directory {name :: Name , entries :: Map.Map String DiskEntry } deriving (Show) -- | The 'mkdir' function creates a new directory inside the given -- directory. Attempts to create a 'DiskEntry' with the name 'name' -- inside the given 'directory'. If an entry with that name already -- exists an error is returned. If such an entry does not exist -- then it is added to the destination 'DiskEntry' and is returned -- as part of a tuple, where the left entry is the old (modified) 'DiskEntry' -- and the right entry is the newly created 'DiskEntry'. mkdir :: Name -> DiskEntry -> Either Error (DiskEntry, DiskEntry) mkdir entryName destination = do if Map.member entryName $ entries destination then do Left $ "mkdir: cannot create directory '" ++ entryName ++ "': File exists" else do let newDirectory = Directory entryName Map.empty d = addToEntries entryName newDirectory destination in Right (d, newDirectory) addToEntries :: Name -> DiskEntry -> DiskEntry -> DiskEntry addToEntries newEntryName newEntry destination = do let updatedEntries = Map.insert newEntryName newEntry (entries destination) d = Directory (name destination) updatedEntries in d ls :: DiskEntry -> [DiskEntry] ls currentDirectory = map snd $ Map.toList $ entries currentDirectory root :: DiskEntry root = Directory "/" Map.empty main :: IO () main = do -- Pretend that we have several "Either Error (DiskEntry, DiskEntry)" -- We know that we have Right values, so get the first result from -- that and extract the first element in the tuple which is the -- newly modified "root" directory. let old = fst $ Either.rights [mkdir "bin" root] !! 0 putStrLn "Connected. Take this shell, and may it serve you well." putStrLn "I will not you create anything yet, but there is a" putStrLn "directory, \"/\" which you are currently in." putStrLn "The `ls` command should let you see what else is there" putStrLn "I will do that for you now" putStrLn $ show $ ls old
which is displayed next
ā ls-adventures git:(master) ā ghci main.hs GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help [1 of 1] Compiling Main ( main.hs, interpreted ) Ok, modules loaded: Main. *Main> :main Connected. Take this shell, and may it serve you well. I will not you create anything yet, but there is a directory, "/" which you are currently in. The `ls` command should let you see what else is there I will do that for you now [Directory {name = "bin", entries = fromList []}]
I am very unhappy with the means for updating directories, I would much prefer my function to be able to update the link directory in place. Is this possible through something like a state monad? Oh, and I guess the code looks awful, like in non-idiomatic Haskell?
shell haskell
Filip allberg
source share