【问题标题】:Haskell beginner how did I get to IO ( IO String )Haskell 初学者我是如何进入 IO ( IO String )
【发布时间】:2018-06-16 00:02:54
【问题描述】:

因此,我开始学习一些 Haskell(无聊的夜班),并编写了一个程序,该程序可以通过计算我在任何给定期间的班次来简化我的假期计划。

import System.Environment
import Data.List
import Data.List.Split
import Data.Time
import Data.Time.Calendar.WeekDate (toWeekDate)


-- merge xs and ys lists alternating value from each [ xs(0),ys(0),(xs(1),ys(1)..xs(n),ys(n) ]
merge :: [a] -> [a] -> [a]
merge xs [] = xs
merge [] ys = ys
merge (x:xs) (y:ys) = x : y : merge xs ys     

-- get part of list from index 'start' to 'stop'
slice :: Int -> Int -> [a] -> [a]
slice start_from stop_at xs = fst $ splitAt (stop_at - start_from) (snd $ splitAt start_from xs)

timeFormat = "%d-%m-%Y"
timeFormatOut :: Day -> String
timeFormatOut = formatTime defaultTimeLocale "%d-%m-%Y" 
-- parses Strings to DateTime Day1
parseMyDate :: String -> Day
parseMyDate = parseTimeOrError True defaultTimeLocale timeFormat

-- 8 week shift rotation
shiftRotation = cycle ["NAT","NAT","NAT","NAT","NAT","NAT","NAT","-","-","-","-","-","-","-","DAG","DAG","DAG","DAG","-","AFT","AFT","-","-","DAG","DAG","DAG","-","-","DAG","DAG","DAG","-","DAG","DAG","DAG","DAG","DAG","-","DAG","DAG","-","-","AFT","AFT","AFT","AFT","AFT","-","-","DAG(r)","DAG(r)","DAG(r)","DAG(r)","DAG(r)","(r)","(r)"]

hs_findshift :: String -> String -> String -> IO String
hs_findshift anchor start end = do
    let dayZero = parseMyDate anchor
    let startDate = parseMyDate start
    let endDate = parseMyDate end
    let startPos = fromIntegral (diffDays startDate dayZero)
    let endPos = fromIntegral (diffDays endDate dayZero) + 1
    let period = slice startPos endPos shiftRotation
    let dates = map (timeFormatOut) [startDate..endDate]
    let listStr = (concat(intersperse "," (merge dates period)))
    putStrLn listStr

这很好用。现在我尝试将其导出到 C# 应用程序,这样我就可以制作一个漂亮的界面。我遇到了一些麻烦。我加了

module Shiftlib where

import Foreign.C.String
import Foreign.C.Types

到顶部。在导入的正下方,我添加了一个块来将输入和输出转换为 C 类型。

foreign export ccall findshift :: CString -> CString -> CString -> IO CString

findshift :: CString -> CString -> CString -> IO CString
findshift a s e = do
    anchor <- peekCString a
    start <- peekCString s
    end <- peekCString e
    result <- hs_findshift anchor start end
    return $ newCString result

现在它无法编译。似乎“return $ newCString result”返回一个“外部”调用不会接受的 IO(IO CString)。

:l shiftlib
[1 of 1] Compiling Shiftlib         ( shiftlib.hs, interpreted )
shiftlib.hs:53:1: error:
    * Illegal foreign declaration: requires unregisterised, llvm (-fllvm) or native code generation (-fasm)
    * When checking declaration:
        foreign export ccall "findshift" findshift
          :: CString -> CString -> CString -> IO CString
   |
53 | foreign export ccall findshift :: CString -> CString -> CString -> IO CString
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

shiftlib.hs:61:5: error:
    * Couldn't match type `IO CString' with `GHC.Ptr.Ptr CChar'
      Expected type: IO CString
        Actual type: IO (IO CString)
    * In a stmt of a 'do' block: return $ newCString result
      In the expression:
        do anchor <- peekCString a
           start <- peekCString s
           end <- peekCString e
           result <- hs_findshift anchor start end
           ....
      In an equation for `findshift':
          findshift a s e
            = do anchor <- peekCString a
                 start <- peekCString s
                 end <- peekCString e
                 ....
   |
61 |     return $ newCString result
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
Failed, no modules loaded.

我似乎无法绕过它。如何从我的小模块返回 CString?

【问题讨论】:

  • merge (x:xs) ys = x : merge ys xs 更好。

标签: haskell types module io c-strings


【解决方案1】:
                newCString :: String -> IO String
                    result :: String
         newCString result ::           IO String
                    return ::                   a -> IO a
return $ newCString result ::                        IO (IO String)

当你已经拥有你想要的东西时,只需停下来,newCString result

【讨论】:

  • 啊。我懂了。所以在返回时再次添加 IO。必须承认我有点认为 IO 只能添加一次 - 无论它是否在 IO 域中。一定是误会了。
  • @JakobBrøgger,这样IO 就像任何其他类型的构造函数一样。就像你可以有一个字符串列表一样,你可以有一个IOIO 字符串。
【解决方案2】:

当你在 IO monad 中工作时,在 foo :: .. -&gt; .. -&gt; IO T 类型的函数中 do 块中的最后一个条目的类型必须为 IO T

return 存在,因此,如果您只有 T,您可以将其包装在 IO 中。本质上,T 类型的表达式计算为 T 类型的值,而没有任何副作用(例如打印消息或变异 IORefs)。 return 将该值转换为 IO T 类型的 IO 计算。 IO T 类型用于在返回 T 之前可能具有副作用的表达式。 return 创建一个没有副作用的计算,作为IO T 的一个小子案例。

newCString result 已经是 IO CString 类型,所以如果你使用 return 你会被包裹太多次,就像 IO (IO CString) 类型的东西一样。这是一个不返回字符串的计算,而是另一个要运行的计算(最终会产生一个字符串)。

这里明显的解决方案是删除return 以避免多余的包装。

另一个不推荐的解决方案是

do ...
   ...
   string <- newCString result
   return string

这会起作用,因为string 的类型是String,而不是IO CString。不建议这样做,因为它是多余的。事实上,我们甚至可以添加更多的冗余

do ...
   ...
   string <- newCString result
   string2 <- return string
   string3 <- return string2
   return string3

这将具有相同的效果。但这没有任何用处,而且会影响可读性。


您的代码完全没有问题。也许您还想考虑一些使用“应用风格”的替代方案。

findshift :: CString -> CString -> CString -> IO CString
findshift a s e = do
    result <- hs_findshift <$> peekCString a <*> peekCString s <*> peekCString e
    newCString result

甚至

findshift :: CString -> CString -> CString -> IO CString
findshift a s e =
    hs_findshift <$> peekCString a <*> peekCString s <*> peekCString e >>= newCString

(不过,我不喜欢最后一个。)

【讨论】:

    猜你喜欢
    • 2011-05-23
    • 2011-07-11
    • 1970-01-01
    • 2019-03-28
    • 2012-03-08
    • 2013-03-25
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多