haskell IO

1,IO类型:跟IO相关的函数返回值放到IO类型容器里,避免跟pure函数混合

Haskell has a special parameterized type called IO. Any value in an IO context must stay in this context. This prevents code that’s pure (meaning it upholds referential transparency and doesn’t change state) and code that’s necessarily impure from mixing.

newtype IO a = GHC.Types.IO

Haskell solves this problem by forcing these two functions to be different types. Whenever a function uses IO, the results of that function are forever marked as coming from IO.

-- pure
mystery1 :: Int -> Int -> Int
mystery1 val1 val2 = (val1 + val2 + val3)^2
    where val3 = 3

-- impure
mystery2 :: Int -> Int -> IO Int
mystery2 val1 val2 = do
    putStrLn "Enter a number" 
    val3Input <- getLine
    let val3 = read val3Input 
    return ((val1 + val2 + val3)^2)

Why does this IO type make your code safer? IO makes it impossible to accidentally use values that have been tainted with I/O in other, pure functions. For example, addition is a pure function, so you can add the results of two calls to mystery1:

 safeValue = (mystery1 2 4) + (mystery1 5 6)

But if you try to do the same thing, you’ll get a compiler error:

 unsafeValue = (mystery2 2 4) + (mystery2 2 4)
 "No instance for (Num (IO Int)) arising from a use of '+'"

Maybe is a parameterized type (a type that takes another type as an argument) that represents a context when a value may be missing. IO in Haskell is a parameterized type that’s similar to Maybe.

The other thing that Maybe and IO have in common is that (unlike List or Map) they describe a context for their parameters rather than a container. The context for the IO type is that the value has come from an input/output operation. Common examples of this include reading user input, printing to standard out, and reading a file.

IO跟Maybe一样定义一个context,包装一个值,避免被包装的值直接跟外界接触

With a Maybe type, you’re creating a context for a single specific problem: sometimes a program’s values might not be there. With IO, you’re creating context for a wide range of issues that can happen with IO. Not only is IO prone to errors, but it’s also inherently stateful (writing a file changes something) and also often impure (calling getLine many times could easily yield a different result each time if the user enters different input). Although these may be issues in I/O, they’re also essential to the way I/O works. What good is a program that doesn’t change the state of the world in some way? To keep Haskell code pure and predictable, you use the IO type to provide a context for data that may not behave the way all of the rest of your Haskell code does. IO actions aren’t functions.

IO操作是有状态的,所以要放到context中跟其他无状态的部分隔离

2,解释hello world

helloPerson :: String -> String
helloPerson name = "Hello" ++ " " ++ name ++ "!"

-- ()表示空的tuple
main :: IO ()
main = do
    putStrLn "Hello! What's your name?"
    name <- getLine
    let statement = helloPerson name
    putStrLn statement

At first () may seem like a special symbol, but in reality it’s just a tuple of zero elements. In the past, we’ve found tuples representing pairs or triples to be useful, but how can a tuple of zero elements be useful? Here are some similar types with Maybe so you can see that IO () is just IO parameterized with (), and can try to figure out why () might be useful:

()类型.png

What type does putStrLn return? It has sent a message out into the world, but it’s not clear that anything meaningful is going to come back. In a literal sense, putStrLn returns nothing at all. Because Haskell needs a type to associate with your main, but your main doesn’t return anything, you use the () tuple to parameterize your IO type. Because () is essentially nothing, this is the best way to convey this concept to Haskell’s type system.

putStrLn什么也不返回,()表示nothing,所以main的类型是IO ()

Although you may have satisfied Haskell’s type system, something else should be troubling you about your main. In the beginning of the book, we stressed three properties of functions that make functional programming so predictable and safe:

 All functions must take a value.
 All functions must return a value.
 Anytime the same argument is supplied, the same value must be returned (referential transparency).

Clearly, main doesn’t return any meaningful value; it simply performs an action. It turns out that main isn’t a function, because it breaks one of the fundamental rules of functions: it doesn’t return a value. Because of this, we refer to main as an IO action. IO actions work much like functions except they violate at least one of the three rules we established for functions. Some IO actions return no value, some take no input, and others don’t always return the same value given the same input.

main不返回值,违反了函数的三个特征,所以main不是函数,main只是 IO action !!!

3,IO actions

-- 没有返回值
putStrLn :: String -> IO ()

-- 没有参数
getLine :: IO String

-- 有参数,也有返回值,但是相同的参数多次调用可能返回不同的值
import System.Random

minDie :: Int
minDie = 1
maxDie :: Int
maxDie = 6

main :: IO ()
main = do
    -- 多次调用,返回不同值
   dieRoll <- randomRIO (minDie, maxDie)
   putStrLn (show dieRoll)

Because I/O is so dangerous and unpredictable, after you have a value come from I/O, Haskell doesn’t allow you to use that value outside of the context of the IO type. For example, if you fetch a random number using randomRIO, you can’t use that value outside main or a similar IO action. You’ll recall that with Maybe you could use pattern matching to take a value safely out of the context that it might be missing. This is because only one thing can go wrong with a Maybe type: the value is Nothing. With I/O, an endless variety of problems could occur. Because of this, after you’re working with data in the context of IO, it must stay there.

IO出错的原因太多,所以IO actions无法超出IO(unsafe,stateful)范围。也即是函数中无法出现IO actions !!!

4,do:do范围内的表达式可以把IO类型当作普通类型

This do-notation allows you to treat IO types as if they were regular types. This also explains why some variables use let and others use <-. Variables assigned with <- allow you to act as though a type IO a is just of type a. You use let statements whenever you create variables that aren’t IO types.

<-赋值的变量可以直接把 a 从 IO a 中取出来
let语句用于给非IO变量赋值

<-.png

5,获取命令行参数

import System.Environment
import Control.Monad

main :: IO ()
main = do
-- 获取命令行参数
args <- getArgs
let linesToRead = if length args > 0
                  then read (head args)
                  else 0 :: Int
-- 重复某个IO action n次
numbers <- replicateM linesToRead getLine
let ints = map read numbers :: [Int]
print (sum ints)
function for IO context.png

6,lazy I/O

toInts :: String -> [Int]
toInts = map read . lines

main :: IO ()
main = do
   -- getContents把输入流看作一个lazy list,直到遇到end
   userInput <- getContents
   let numbers = toInts userInput
   print (sum numbers)

7,Text类型:性能比String好,通过pack把String转成Text,unpack把Text转成String

8,文件读写

import System.IO

-- 定义
-- openFile :: FilePath -> IOMode -> IO Handle
-- type FilePath = String
-- data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode

main :: IO ()
main = do
    -- 打开文件
    helloFile <- openFile "hello.txt" ReadMode
    -- 读文件
    firstLine <- hGetLine helloFile
    putStrLn firstLine

    secondLine <- hGetLine helloFile
    goodbyeFile <- openFile "goodbye.txt" WriteMode
    -- 写文件
    hPutStrLn goodbyeFile secondLine

    -- 关闭文件
    hClose helloFile
    hClose goodbyeFile

    putStrLn "done!"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,254评论 0 10
  • 文/公羽小米 星唱苍穹子醉英雄深闺梦女红 月隐深宫竹影笑风灯花把梦争 梦归处几世英雄几回红 (已发表于新浪博客公羽小米)
    胖橘阅读 261评论 0 2
  • 书里的四个小伙伴,越看越觉得鲜活。每一个都是如此的灵动机警,这一次木头又利用妈妈送手机的事情,给爸爸上了一课...
    倔强不止的老头阅读 248评论 0 1