我正在尝试使用Haskell进行虚拟shell体验.我的意思是,我的意思是创建一个程序,当从终端窗口执行时,将您带入一个环境,允许您执行熟悉的命令的子集(例如,cd
和mkdir
)等.
文件系统将完全是虚拟的,因为它不会触及磁盘上的任何内容.
我的初始草图(下图)感觉很笨,事实上,组成一个MWE是一个拖累,我非常不习惯在功能上表示数据结构,并希望得到一些关于如何扭转这种体验的指针.任何有可用源代码的参考程序都会有所帮助,任何提示或思路都会有所帮助.
我写了以下内容
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
输出到以下
? 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 []}]
我对更新目录的方法非常不满意,我更希望我的函数能够在适当的位置更新引用的目标目录.这可能通过State Monad这样的东西来实现吗?哦,我认为代码看起来很可怕,就像非惯用的Haskell一样?