fugafuga.write

日々のログ

すごいH本 part58

I/O アクションの中で let 使う

let はI/Oアクションの中で普通の値に名前を与えたいときに使う。

import Data.Char

main = do
    putStrLn "Hello, what's your first name?"
    firstName <- getLine
    putStrLn "What's your last name?"
    lastName <- getLine
    let bigFirstName = map toUpper firstName
        bigLastName = map toUpper lastName
    putStrLn $ "Hey " ++ bigFirstName ++ " "
                      ++ bigLastName
                      ++ ", how are you?"

実行

*Main> main
Hello, what's your first name?
john
What's your last name?
mayer
Hey JOHN MAYER, how are you?
  • <- は I/Oアクションの結果に名前を束縛したい時に使う
  • let は 純粋な式に名前を束縛したい時に使う

逆順に表示

入力された単語を逆さまにして表示するプログラムを作る

main = do
    line <- getLine
    if null line
        then return ()
        else do
            putStrLn $ reverseWords line
            main

reverseWords :: String -> String
reverseWords = unwords . map reverse . words

ビルドする

> stack ghc -- --make reverse.hs
[1 of 1] Compiling Main             ( reverse.hs, reverse.o )
Linking reverse ...

実行する

> ./reverse
123456
654321
252525252525
525252525252
reverse
esrever
  1. 入力に対して words を適用、単語のリストを作る
  2. 単語のリストに対してreverseを適用
  3. unwordsでリストを文字列に戻す

Haskell の return は、純粋な値から I/O アクションを作り出すために利用される。 return はプログラムを中断し結果を返すのではなく、値を受け取り箱の中に入れるもの。

所感

インタラクティブなプログラムが動いて感動している。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る

すごいH本 part57

入出力

前回はただのポエムになってしまったので真面目にやっていく。

出力するのに使った関数 putStrLn

*Main Lib> :t putStrLn
putStrLn :: String -> IO ()
*Main Lib> :t putStrLn "hello, world"
putStrLn "hello, world" :: IO ()

putStrLn の型は、文字列を引数に取り、() 空のタプルを結果とする I/O アクションを返す。 I/O アクションとは、実行されると副作用を含む動作をして返すような何か。 ここでいう副作用とは、入力を読んだり画面やファイルに何かを書き出したりすること。 文字列を端末に表示するアクションには実際には意味のある返り値がないので、 ダミーとして () を使っている。

このことを I/O アクションが結果を生成する。という。 I/O アクションは、main という名前をつけてプログラムを起動すると実行される。

putStrLn "hello, world"は評価されると、hello, worldと出力するのではなく、 hello, world を表示しろ という命令を返している。

I/O アクション同士をまとめる

複数の I/O アクションを1つにするのに do 構文を使う。

main = do
    putStrLn "Hello, what's your name?"
    name <- getLine
    putStrLn ("Hey " ++ name ++ ", you rock!!!")

命令型のプログラムのように実行ステップを連ねて書ける。 実行ステップそれぞれが I/O アクションとなっている。

getLineを見る

*Main> :t getLine
getLine :: IO String

String を生成する I/O アクションだとわかる。 name <- getLline で入力された何かを name に束縛している。

I/O アクションは小さな足がついた箱のようなもの。 実世界に出ていって何かを行い、何か値を持って帰ってくる。 箱がデータを取ってきたとき、それを明けて中のデータを入手する唯一の方法が<-となる。

このようにして、Haskell は純粋なものと不純なものを分類している。

getLine は純粋ではない。 なぜなら、2回実行したときに同じ結果を返す保証が無いから。

次のコードは動作しない。

nameTag = "Helllo, my name is " ++ getLline

++の2つめの引数はString型を要求するが、

*Main> :t (++)
(++) :: [a] -> [a] -> [a]

getLine は、IO String型になるため型が一致しない。 動作させるためには、どこか別の I/O アクションの中で name <- getLine するしかない。

純粋でないデータを扱いたい場合は、純粋でない環境の中で行う必要がある。 不純による汚染を防ぐために、コード中のI/Oの部分を可能な限り小さくするべき。

  • I/O アクションから値を取り出すには <- を使って名前に束縛するしかない
  • I/O アクションが実行される時
    • main という名前を与えられた時
    • do ブロックで作った別の大きな I/O アクションの中にある時
    • GHCi に I/O アクションを入力してEnterを押した時
  • I/O アクションを do でまとめ、そのまとめられた I/O アクションを他の do で使える

所感

純粋な世界と不純な世界がはっきりと分けて考えられている。 この考え方は今までなかったのでとても新鮮だし、それを強制されるという部分が興味深い。

I/O アクションちゃんはあっちの世界とこっちの世界を行ったり来たりしてて働き者でかわいい。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る

すごいH本 part56

こんにちは、世界。 〜時を超えて〜

世界との邂逅

HelloWorld.hs

main = putStrLn "hello, world"

コンパイルして実行ファイルを生成する

が、stack を使っているのでH本の通りにやってもできない。 以下のようにするとビルドできた。

> stack ghc -- --make helloworld
[1 of 1] Compiling Main             ( helloworld.hs, helloworld.o )
Linking helloworld ...

去年の12月から初めて早2ヶ月

晴れの日も雨の日も雪の日も大晦日でさえH本をこなしてきました。

そんな私にもやっと世界と対話する機会が与えられました。

神様、お父さん、お母さん、先生、かわいい動物、そのへんの虫。

全てにありったけの感謝をこめてこれを捧げます。

――それでは、聴いて下さい

> ./helloworld
hello, world

以上です、ありがとうございました(号泣)

(参考)

https://stackoverflow.com/questions/36254337/stack-ghc-error-invalid-option-make

所感

号泣

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る

すごいH本 part55

型の種類

型とは、値についている何らかの推論をするためについている小さなラベルである。 それと同じように型にも小さなラベルが付いている。 型に付くラベルのことを種類(kind)という。

種類を調べる

*Main Lib> :k Int
Int :: *

* は具体型を表す記号。 具体型とは型引数をとらない型のこと。

Maybeを見る

*Main Lib> :k Maybe
Maybe :: * -> *

具体型をとって -> 具体型を返す 型コンストラクタであることがわかる。

*Main Lib> :k Maybe Int
Maybe Int :: *

型引数を1つ与えると具体型になる。

Eitherを見る

*Main Lib> :k Either
Either :: * -> * -> *

2つの具体型をとって具体型を返す関数である。 部分適用してみる。

*Main Lib> :k Either String
Either String :: * -> *
*Main Lib> :k Either String Int
Either String Int :: *

Functor になれる型は種類が * -> * の型なので、 Either を部分適用して * -> * -> * から * -> * に変換する必要があった。

所感

全てが関数でできている感が得られた。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る

すごいH本 part54

Map を Functor のインスタンスにする

練習問題とされていたので自分で実装してみる

Map,Functorはすでに定義されているので独自のデータ型とFunctorを作る

data MyMap k v = MyMap k v deriving (Show)

class MyFunctor f where
        fmap' :: (a -> b) -> f a -> f b

instance MyFunctor (MyMap k) where
        fmap' f (MyMap k v) = MyMap k (f v)

実行する

*Main> let m = MyMap "key" 2
*Main> fmap' (*3) m
MyMap "key" 6

所感

なんとかいけたのではないだろうか...

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

  • 作者: MiranLipovaca
  • 出版社/メーカー: オーム社
  • 発売日: 2017/07/14
  • メディア: Kindle版
  • 購入: 4人 クリック: 9回
  • この商品を含むブログを見る