映画観てとても良かったんで原作も読み返してた。以前は上巻しか読んでなくて、言葉少ないし状況がよう分からん漫画じゃのうと思ってたが、映画見たあとならすんなり読める。遊郭の姉さんと実は過去の関係があったりで映画では省かれた話もあり、映画も原作も両方お勧め。

むかーし週報に書いた内容を、同僚が覚えていてくれて、嬉しかったんだけど自分はほぼ忘れかけていた。
まあ適当にでも情報を発信していれば、それに反応してくれる人が出てきて楽しいし、自分の考えも整理できて良いことだなと思った。

朝香宮邸の亡霊

www.teien-art-museum.ne.jp

朝香宮邸。
f:id:kawauso7c:20161005204135j:plain
f:id:kawauso7c:20161005204204j:plain

作品が雰囲気ある邸宅内に展開されていて最高でした。
最近はインスタレーションアートに興味があります。自分の感覚をハックされている気分になります。

mainでputStrをmapする方法

前回分からなかった件、簡単に書くとこんな感じ。

main = mapM putStrLn ["hoge", "piyo", "fuga"]

mapでも良さそうな気がしてしまうが駄目。mapMでもmapM_でも問題なく動くようなのだが。理由を自分なりにまとめた。

putStrLn

Prelude> :t putStrLn
putStrLn :: String -> IO ()

putStrLnは「文字列を引数に取り、()(空のタプル)を結果とするI/Oアクションを返す」。

アクションとは

Haskellの関数は全て参照透過性(同じ引数に対して常に同じ値を返す)がある。アクションは参照透過性がないものを扱う。例えば乱数を返すアクション、コンソールからの入力を返すアクション等。

I/Oアクションとは

実行されると、入力を読んだり画面やファイルに何かを書き出したりする動作をして、結果を返すアクション。

putStrLnが「()を結果とするI/Oアクション」を返すのは、文字列を端末に表示するアクションには意味のある返り値がないので、ダミーの値として()を使うため。*1

main変数

Mainモジュールに関係がある。

モジュールとは

エンティティ(変数、型コンストラクタ、データコンストラクタ、フィールドラベル、型クラス、クラスメソッド)をパッケージ化したもので、モジュールごとに名前空間が分かれている。

importで別モジュールをインポートできる。

import System.Random

モジュールを定義するには以下のように書く。

module MyFileUtils (tmpPath) where

MyFileUtilsモジュールを定義し、tmpPathエンティティを外部にエクスポートする宣言となる。

ファイル内でmodule宣言を省略すると、以下のmodules宣言が勝手に追加される。

module Main (main) where

このmainがプログラムエントリーポイントとなり実行される。 mainはI/Oアクションが束縛された変数でなければならない。

map、mapM

ここまで、main :: IO aputStrLn :: String -> IO ()となることを説明した。つまり

main = putStrLn "hoge"

これは型が合致し問題なく動くということだ。では間にmapを挟むにはどうしたらいいか。
sequence関数というのがある。I/Oアクションのリストを引数にし、それらを順に実行するI/Oアクション(シーケンス)を返す。これをつかって以下のように書ける。

main = sequence $ map putStrLn ["hoge", "piyo", "fuga"]

mapが[putStrLn "hoge", putStrLn "piyo", putStrLn "fuga"]を返すのでsequnceがI/Oアクションにまとめる訳だ。
mapM関数はシーケンスにまとめる処理を内部で行ってくれる。

main = mapM putStrLn ["hoge", "piyo", "fuga"]

結論として、mapMは、リストに対してI/Oアクションを返す関数をマップし、単一のI/Oアクションにまとめてくれるから、mainに束縛できるということだ。
最後に残ったmapM_、動作はmapMとほぼ同じで、違いはmapM_はI/Oアクションの結果を捨てる。

ちなみにsequenceとmapMのシグネチャは相変わらず読めない。

Prelude> :t sequence
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
Prelude> :t mapM
mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)

参考にした。
http://jutememo.blogspot.jp/2010/03/haskell-mapm-foldr.html
すごいHaskellたのしく学ぼう!

*1:voidなんて無いんだね、と思ったらある?

Haskellで作る奇妙なプログラミング言語 Haskell練習問題②

これの続き。

問3 数を1ずつカウントしていく何かを作ってください。

inc :: Int -> Int
inc c = c + 1

main = do
  print $ inc $ inc $ inc $ inc 0

もともとの問題は、incメソッドを呼び出されるたび数をインクリメントしていくクラスを作れ、なので、副作用を扱わないといけないが、分からんのでお茶を濁す。

問4 自分自身のソースコードを出力するHaskellプログラムを書いてください。

main = do
  str <- readFile "problem4.hs"
  putStr str

こちらは簡単。

$ cat problem4.hs
-- 自分自身のソースコードを出力するHaskellプログラムを書いてください。

main = do
  str <- readFile "problem4.hs"
  putStr str
$ runhaskell problem4.hs
-- 自分自身のソースコードを出力するHaskellプログラムを書いてください。

main = do
  str <- readFile "problem4.hs"
  putStr str

ソースコードは以下。
https://github.com/kawausokun/esoteric-language-in-haskell

Hakellの型シグネチャを読む練習

ひたすら読む。読めねばならぬ。ghciを起動し:tだ。

Prelude> :t 'h'
'h' :: Char

'h'は「Char型」。

Prelude> :t "hoge"
"hoge" :: [Char]

"hoge"は「Char型のリスト」である。

Prelude> :t (True, 'a')
(True, 'a') :: (Bool, Char)

(True, 'a')は「タプル(Bool, Char)型」である。

Prelude> :t 4 == 5
4 == 5 :: Bool

4 == 5は「Bool型」である。

Prelude> import Data.Char
Prelude Data.Char> :t toLower
toLower :: Char -> Char

toLowerは「Charを引数としCharを返す関数」である。

Prelude> :t map
map :: (a -> b) -> [a] -> [b]

a,bは型変数であり、どんな型も取り得る。シグネチャ内で一つの型変数は常に同じ型を取る。
mapは「aを受けbを返す関数と[a]型を引数とし、[b]型を返す関数」である。
(この()がタプルに見えるんだよな。)

Prelude> import Data.List
Prelude Data.List> :t sort
sort :: Ord a => [a] -> [a]

sortは「[a]型を引数とし[a]型を返す関数、ただしaは型クラスOrdのインスタンス」である。
ある型が型クラスの制約を満たすとき、その型は型クラスのインスタンスである、と言う。
Ordクラスの定義を覗くと

Prelude> :i Ord
class Eq a => Ord a where
  compare :: a -> a -> Ordering
  (<) :: a -> a -> Bool
  (<=) :: a -> a -> Bool
  (>) :: a -> a -> Bool
  (>=) :: a -> a -> Bool
  max :: a -> a -> a
  min :: a -> a -> a

compare,(<),(<=)...関数を実装しろという制約。Eqクラスを継承しているのでそちらの制約もある。

mapMの型シグネチャが読めなかったので今はここまで。

Haskellで作る奇妙なプログラミング言語 Haskell練習問題編

Rubyで作る奇妙なプログラミング言語Haskellで追った。

最初の章は言語の練習。もう勉強したことを忘れてきているのでちょうどいい。

1-1 Haskell練習問題

問1 画面に「Hello, world!」という文字列を出力するHaskellプログラムを書いてください。

main = do putStrLn "Hello, world!"

問2 「99 bottles of beer」の全文を出力するHaskellプログラムを書いてください。

verse :: Integer -> String
verse 0 = "No More bottles of beer on the wall, no more bottles of beer.\nGo to the store and buy some more, 99 bottoles of beer on the wall."
verse n = bottles n ++ " of beer on the wall, " ++ bottles n ++ " of beer.\nTake one down and pass it around, " ++ bottles (n-1) ++ " of beer on the wall."
  where bottles 0 = "no more bottles"
        bottles 1 = "1 bottle"
        bottles n = show n ++ " bottles"

main = mapM putStrLn $ map verse [99,98..0]

mapMのところが何故こう書くのか分からない。後でIOとモナドを調べなおす。
上級者の回答。
http://www.99-bottles-of-beer.net/language-haskell-1070.html
Alternative Versionsが全く読めない。

ソースコードは以下。
https://github.com/kawausokun/esoteric-language-in-haskell