2016-06-20 3 views
3

this question의 후속 조치입니다. 나는 shell을 @ ErikR의 answer에서 내 InputT 루프에 결합하려고합니다.StateT와 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 

그리고 오류

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 } } 

전체 코드를 얻기가 here을 찾을 수 있습니다, 그것은 Write you a haskell을 기반으로.

나는 haskelline에 내역이 내장되어 있음을 알고 있지만 연습으로 직접 구현하려고합니다.

동일한 기능을 얻기 위해 모나드 변환기의 대체품을 자유롭게 제안 할 수 있습니다.

내 진짜 문제

내가 쓰기에 람다 REPL에 기능, 즉 당신은 하스켈처럼 ipython을 추가 할

:

I. 입력 및 출력에 대한 카운터가 나타납니다 즉,

In[1]> 
Out[1]> 

이것은 이미 done입니다.

II. 각 명령을 기록에 저장 (자동)하고 특수 명령을 사용하여 이전 명령을 모두 표시합니다 (예 : histInput (histipython과 동일). 또한 모든 출력 결과의 내역을 저장하고 histOutput을 사용하여 표시하십시오. 이것은 내가이 질문 (입력 히스토리에 대해서만 잠시 동안)에서하려고하는 것입니다.

III. 이전의 입력 및 출력을 참조하십시오. In[1]x 인 경우 In[1] + 2x + 2으로, 출력용으로는 마찬가지입니다.

업데이트

나는 ErikR의 answer @ 결합하려고했습니다, 일시적으로는 함께 올라오고, showStep을 사용할 수 없습니다 : 아직 컴파일되지 않는

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 

:

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 이미 당신을 위해 명령 줄 역사를 구현 -에서 [사용 예]를 살펴 (이 https://hackage.haskell.org/ package/haskeline-0.7.2.3/docs/System-Console-Haskeline.html) – ErikR

+0

고마워, 알아,하지만 운동으로 직접 구현하고 싶다. – dimid

+0

히스토리를 직접 구현하려면 왜 코드에 'InputT'가 표시됩니까? – ErikR

답변

1

첫 번째 오류는 사용자가 선언했기 때문입니다.

,210
main :: IO() 

하지만 또한

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

execStateT는 계산의 최종 상태를 반환하고 상태 유형 [String]이다. 보통 이것은 main에 대한 유형을 선언하지 않고 단지 a에 대해 IO a 인 것으로 추론함으로써 해결됩니다. 두 번째 것은 확실하지 않지만 어쩌면 똑같은 것일 수 있습니다.

+0

고마워. 첫 번째 오류는'main :: IO [String] '로 바뀌어 해결되었다. 나는 그 질문을 갱신 할 것이다. – dimid

1

나는 당신이하려고하는 것에 맞춰 추측 할 것입니다.

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 

프로그램 소스 :

이 프로그램은 다음과 같은 명령을 인식

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

고마워, 내 진짜 문제에 대한 설명을 추가하겠습니다. – dimid

0

당신이 here 컴파일이 코드를, 그리고 같은 process 정의

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

것은 만들 수 process의 버전

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

그냥 liftIO 사용이 서명

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

감사합니다, 그것은 컴파일하지만 역사 논리가 부족합니다. 어떻게 추가 하시겠습니까? 특히, 상태 모나드 안에 있지 않거나 뭔가 빠져 있다면'showHistory'에서'get'을 어떻게 사용할 수 있습니까? – dimid

+0

나는 또한 'process'에서'showHist'를 호출하려고 시도했고, 또 다른 [error] (http://stackoverflow.com/q/38071560/165753)에 의해 걸렸습니다. – dimid