2016-06-20 13 views
3

Es ist ein Follow-up zu this question. Ich versuche zu kombinieren shell von @ ErikR answer in meinem InputT Schleife.kombiniert StateT mit InputT

main :: IO [String] 
main = do 
    c <- makeCounter 
    execStateT (repl c) [] 

repl :: Counter -> StateT [String] IO() 
repl c = lift $ runInputT defaultSettings loop 
    where 
    loop = do 
    minput <- getLineIO $ in_ps1 $ c 
    case minput of 
     Nothing -> lift $ outputStrLn "Goodbye." 
     Just input -> (liftIO $ process c input) >> loop 

getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String) 
getLineIO ios = do 
    s <- liftIO ios 
    getInputLine s 

Und einen Fehler bekommen

Main.hs:59:10: 
    Couldn't match type ‘InputT m0’ with ‘IO’ 
    Expected type: StateT [String] IO() 
     Actual type: StateT [String] (InputT m0)() 
    Relevant bindings include 
     loop :: InputT (InputT m0)() (bound at Main.hs:61:3) 
    In the expression: lift $ runInputT defaultSettings loop 
    In an equation for ‘repl’: 
     repl c 
      = lift $ runInputT defaultSettings loop 
      where 
       loop 
       = do { minput <- getLineIO $ in_ps1 $ c; 
         .... } 

Main.hs:62:5: 
No instance for (Monad m0) arising from a do statement 
The type variable ‘m0’ is ambiguous 
Relevant bindings include 
    loop :: InputT (InputT m0)() (bound at Main.hs:61:3) 
Note: there are several potential instances: 
    instance Monad (Text.Parsec.Prim.ParsecT s u m) 
    -- Defined in ‘Text.Parsec.Prim’ 
    instance Monad (Either e) -- Defined in ‘Data.Either’ 
    instance Monad Data.Proxy.Proxy -- Defined in ‘Data.Proxy’ 
    ...plus 15 others 
In a stmt of a 'do' block: minput <- getLineIO $ in_ps1 $ c 
In the expression: 
    do { minput <- getLineIO $ in_ps1 $ c; 
     case minput of { 
     Nothing -> lift $ outputStrLn "Goodbye." 
     Just input -> (liftIO $ process c input) >> loop } } 
In an equation for ‘loop’: 
    loop 
     = do { minput <- getLineIO $ in_ps1 $ c; 
      case minput of { 
       Nothing -> lift $ outputStrLn "Goodbye." 
       Just input -> (liftIO $ process c input) >> loop } } 

Der vollständige Code kann here gefunden werden, wird es auf Write you a haskell basiert.

Ich weiß, haskelline hat eine integrierte Unterstützung für die Geschichte, aber ich versuche, es selbst als Übung zu implementieren.

Fühlen Sie sich frei, Ersatz für die Monade-Transformatoren vorschlagen, um die gleiche Funktionalität zu erhalten.

mein eigentliches Problem

ich ipython wie Fähigkeiten, um die Lambda-REPL in Write hinzufügen möchten Sie ein Haskell, nämlich:

I. Ein Zähler für Ein- und Ausgabe, die angezeigt wird, in der Aufforderung, dh

In[1]> 
Out[1]> 

Dies ist bereits done.

II. Speichern Sie jeden Befehl im Verlauf (automatisch) und zeigen Sie alle vorherigen Befehle mit einem speziellen Befehl an, z. histInput (wie hist in ipython). Speichern Sie außerdem einen Verlauf aller Ausgabeergebnisse und zeigen Sie sie unter Verwendung von histOutput an. Dies ist, was ich versuche, in dieser Frage zu tun (Eingabehistorie nur für den Moment).

III. Bezug auf vorherige Eingaben und Ausgaben, z. Wenn In[1]x war, dann sollte In[1] + 2 durch x + 2 ersetzt werden, und ebenso für die Ausgabe.

aktualisieren

Ich habe answer @ ErikR des kombinieren versucht, und vorübergehend showStep deaktiviert, mit kommen:

module Main where 

import Syntax 
import Parser 
import Eval 
import Pretty 
import Counter 

import Control.Monad 
import Control.Monad.Trans 
import System.Console.Haskeline 
import Control.Monad.State 

showStep :: (Int, Expr) -> IO() 
showStep (d, x) = putStrLn ((replicate d ' ') ++ "=> " ++ ppexpr x) 

process :: Counter -> String -> InputT (StateT [String] IO)() 
process c line = 
    if ((length line) > 0) 
     then 
     if (head line) /= '%' 
      then do 
       modify (++ [line]) 
       let res = parseExpr line 
       case res of 
        Left err -> outputStrLn $ show err 
        Right ex -> do 
         let (out, ~steps) = runEval ex 
         --mapM_ showStep steps 
         out_ps1 c $ out2iout $ show out 
     else do 
       let iout = handle_cmd line 
       out_ps1 c iout 

    -- TODO: don't increment counter for empty lines 
    else do 
     outputStrLn "" 

out2iout :: String -> IO String 
out2iout s = return s 

out_ps1 :: Counter -> IO String -> InputT (StateT [String] IO)() 
out_ps1 c iout = do 
     out <- liftIO iout 
     let out_count = c 0 
     outputStrLn $ "Out[" ++ (show out_count) ++ "]: " ++ out 
     outputStrLn "" 

handle_cmd :: String -> IO String 
handle_cmd line = if line == "%hist" 
        then 
         evalStateT getHist [] 
        else 
         return "unknown cmd" 

getHist :: StateT [String] IO String 
getHist = do 
    hist <- lift get 
    forM_ (zip [(1::Int)..] hist) $ \(i, h) -> do 
           show i ++ ": " ++ show h 

main :: IO() 
main = do 
    c <- makeCounter 
    repl c 

repl :: Counter -> IO() 
repl c = evalStateT (runInputT defaultSettings(loop c)) [] 

loop :: Counter -> InputT (StateT [String] IO)() 
loop c = do 
    minput <- getLineIO $ in_ps1 $ c 
    case minput of 
     Nothing -> return() 
     Just input -> process c input >> loop c 

getLineIO :: (MonadException m) => IO String -> InputT m (Maybe String) 
getLineIO ios = do 
    s <- liftIO ios 
    getInputLine s 

in_ps1 :: Counter -> IO String 
in_ps1 c = do 
    let ion = c 1 
    n <- ion 
    let s = "Untyped: In[" ++ (show n) ++ "]> " 
    return s 

, die noch nicht kompiliert:

Main.hs:59:5: 
    Couldn't match type ‘[]’ with ‘StateT [String] IO’ 
    Expected type: StateT [String] IO String 
     Actual type: [()] 
    In a stmt of a 'do' block: 
     forM_ (zip [(1 :: Int) .. ] hist) 
     $ \ (i, h) -> do { show i ++ ": " ++ show h } 
    In the expression: 
     do { hist <- lift get; 
      forM_ (zip [(1 :: Int) .. ] hist) $ \ (i, h) -> do { ... } } 
    In an equation for ‘getHist’: 
     getHist 
      = do { hist <- lift get; 
       forM_ (zip [(1 :: Int) .. ] hist) $ \ (i, h) -> ... } 
+0

haskeline bereits implementiert Geschichte Befehlszeile für Sie - haben einen Blick auf die [Anwendungsbeispiel] (https://hackage.haskell.org/ package/haskeline-0.7.2.3/docs/System-Konsole-Haskeline.html) – ErikR

+0

Danke, ich weiß, aber ich möchte es selbst als Übung implementieren. – dimid

+0

Wenn Sie Geschichte selbst implementieren möchten, warum erscheint 'InputT' in Ihrem Code? – ErikR

Antwort

1

Der erste Fehler ist, weil Sie

deklariert haben
main :: IO() 

aber auch

execStateT (...) :: IO [String] 

execStateT gibt den Endzustand der Berechnung, und Ihr Zustand ist vom Typ [String]. In der Regel wird dies behoben, indem einfach kein Typ für main deklariert wird, und es wird gefolgert, dass es IO a für einige a ist. Der zweite ist mir nicht sicher, aber vielleicht ist es dasselbe.

+0

Danke, der erste Fehler wurde behoben, indem man in 'main :: IO [String] ' wechselte. Ich werde die Frage aktualisieren. – dimid

1

Ich nehme an, was Sie versuchen zu tun.

Dieses Programm erkennt die folgenden Befehle:

hist  -- show current history 
add xxx  -- add xxx to the history list 
clear  -- clear the history list 
count  -- show the count of history items 
quit  -- quit the command loop 

Programm Quelle:

import System.Console.Haskeline 
import Control.Monad.Trans.Class 
import Control.Monad.Trans.State.Strict 
import Control.Monad 

main :: IO() 
main = evalStateT (runInputT defaultSettings loop) [] 

loop :: InputT (StateT [String] IO)() 
loop = do 
    minput <- getInputLine "% " 
    case minput of 
     Nothing -> return() 
     Just "quit" -> return() 
     Just input -> process input >> loop 

process input = do 
    let args = words input 
    case args of 
    [] -> return() 
    ("hist": _)  -> showHistory 
    ("add" : x : _) -> lift $ modify (++ [x]) 
    ("clear": _) -> lift $ modify (const []) 
    ("count": _) -> do hs <- lift get 
          outputStrLn $ "number of history items: " ++ show (length hs) 
    _    -> outputStrLn "???" 

showHistory = do 
    hist <- lift get 
    forM_ (zip [(1::Int)..] hist) $ \(i,h) -> do 
    outputStrLn $ show i ++ " " ++ h 
+0

Vielen Dank, ich werde eine Beschreibung meines wirklichen Problems hinzufügen. – dimid

0

Der Code, den Sie here compiliert haben, und es definiert process als:

process :: Counter -> String -> IO() 

erstellen eine Version von process Mit dieser Unterschrift:

Counter -> String -> InputT (StateT [String] IO)() 

verwenden nur liftIO:

process' :: Counter -> String -> InputT (StateT [String] IO)() 
process' counter str = liftIO $ process counter str 
+0

Danke, es kompiliert, aber es fehlt die History-Logik. Wie würden Sie vorschlagen, es hinzuzufügen? Genauer gesagt, wie kann ich 'get' in' showHistory' verwenden, wenn es nicht in einer Status-Monade ist oder fehlt mir etwas? – dimid

+0

Ich habe auch versucht, 'showHist' von' process' zu nennen, und bin von einem anderen [Fehler] festgefahren (http://stackoverflow.com/q/38071560/165753). – dimid

Verwandte Themen