HaskellでSchemeを実装する(エラーの導入)

Write Yourself a Scheme in 48 Hoursを読んでいる。4章のエラーチェッキングのあたり。

LispErrorを定義して、それをHaskellの組み込みのエラー関数で使えるようにする。

MonadErrorのインスタンスにすればthrowErrorとcatchErrorが使えるが、そのためにはEither型の左側をErrorクラスのインスタンスにする必要がある。

instance Error e => MonadError e (Either e) where
    throwError             = Left
    Left  l `catchError` h = h l
    Right r `catchError` _ = Right r

コード中ではここ。

type ThrowsError = Either LispError

instance Show LispVal where show = showVal
instance Show LispError where show = showError
instance Error LispError where
     noMsg = Default "An error has occurred"
     strMsg = Default

typeで別名をつけているのは取り回しを楽にするためかな?Error モナドを読んでも

type ParseMonad = Either ParseError

ってやってるし、エラーを取り扱う場合のお作法なのかなぁ。

モナド変換子を重ねまくれ

すごいHaskellのモナド変換子を理解したら読めばいいと思います、というか読むべき。超わかりやすかった。合体ロボ並に合成されて強そうな型に変換していく様は圧巻のゴーカイジャー(見てないからしらんけど)

スマイルプリキュアで例えると、「プリキュア・レインボーヒーリング」ですね(多分)。

最初に簡単な評価器からはじめる

eval0 :: Env -> Exp -> Value

という型から始めてモナドにする。

type Eval1  a = Identity a

次にエラーモナド変換子を重ねて

type Eval2  a = ErrorT String Identity a

環境であるReaderTを重ねる

type Eval3 a = ReaderT Env (ErrorT String Identity) a

続いて、状態を重ねる

type Eval4 a = ReaderT Env (ErrorT String (StateT Integer Identity)) a

ログ機能を導入して

type Eval5 q = ReaderT Env (ErrorT String
  (WriterT [String ] (StateT Integer Identity))) q

最後にお約束のIO

type Eval6 a = ReaderT Env (ErrorT String
  (WriterT [String ] (StateT Integer IO))) a

順番にテンポよく積み上がっていくので、読んでて悩むことなく最後の型まで到達できるので素晴らしい。英語も平易だし。

さらっと読んでみてよく分からなかったら、すごいHaskellに立ち戻ればいいでしょう。

ProductName すごいHaskellたのしく学ぼう!
Miran Lipovača
オーム社 / 2940円 ( 2012-05-23 )


Fedora17で仮想化

ヒトと技術が減少していて、サーバーメンテにコストをかけられなくなってきているので、実機のトラブル時の移行コストがかかりすぎて「移行しないでいいかー」という投げやりな事案が多く発生するようになり、資産がもったいないので自分の管理サーバーは仮想化環境でなんとかすることにした。

本見ながらやったけど、色々試してたらネットワークが繋がらなくなってどうしようもなくなったのでホストを再インストールしたという。

ProductName KVM徹底入門 Linuxカーネル仮想化基盤構築ガイド
平 初
翔泳社 / 3444円 ( 2010-07-08 )


ホストのインストールとか詳しくやらんでいいから、もっとネットワーク周りを充実させて欲しいなぁと思った。 私のニーズはサーバー群を集約して、複数のwebサーバーを一台で管理したいというニーズだったんだが、そこら辺の記述が妙に少ないなぁと。

ブリッジを使ったネットワーク環境の構築

まずはホストの設定、動くのを確認したらゲストをゆっくり設定すると問題の切り分けが簡単。

  1. ホストだけでネットワークの設定をして外部につながることを確認。
  2. /etc/sysconfig/network-scripts/のifcfg-em1をコピーしてブリッジの設定をしたらネットワークを再起動してつながることを確認。プロミスキャスモードの必要はない。

ネットワークマネージャの管理下ではなくなるのでFedora17の場合は画面右上のネットワークアイコンに☓がつくがつながることを確認すればOK

  1. ゲストOSをインストールするときにハードウェアのアドバンス設定にブリッジが出るので、本のようにドメイン設定をvirshで行わなくてもOK

その他トラブルシューティング

libvirtdの軌道はsystemctl

systemctl start libvirtd.service

sshの設定は

yum install openssh-server

つながらない場合はFireWallを疑う。GUIの設定が正しいように見えても再度sshのチェックを付け直すと上手くいったりする。

というわけで、virshの便利さに驚いた6月後半であった。

HaskellでSchemeを実装する

Paesecの使い方の勉強も兼ねてWrite Yourself a Scheme in 48 Hoursをやりはじめた。

Parser書くとこまではすんなり終了。

import System.Environment
import Text.ParserCombinators.Parsec hiding (spaces)
import Control.Monad

data LispVal = Atom String
             | List [LispVal]
             | DottedList [LispVal] LispVal
             | Number Integer
             | String String
             | Bool Bool

instance Show LispVal where show = showVal

symbol :: Parser Char
symbol = oneOf "!#$%&|*+-/:<=>?@^_~"

spaces :: Parser ()
spaces = skipMany1 space

parseString :: Parser LispVal
parseString = do char '"'
                 x <- many (noneOf "\"")
                 char '"'
                 return $ String x

parseAtom :: Parser LispVal
parseAtom = do first <- letter <|> symbol
               rest <- many (letter <|> digit <|> symbol)
               let atom = first:rest
               return $ case atom of 
                          "#t" -> Bool True
                          "#f" -> Bool False
                          _    -> Atom atom

parseNumber :: Parser LispVal
parseNumber = liftM (Number . read) $ many1 digit

parseList :: Parser LispVal
parseList = liftM List $ sepBy parseExpr spaces

parseDottedList :: Parser LispVal
parseDottedList = do
  head <- endBy parseExpr spaces
  tail <- char '.' >> spaces >> parseExpr
  return $ DottedList head tail

parseQuoted :: Parser LispVal
parseQuoted = do
  char '\''
  x <- parseExpr
  return $ List [Atom "quote", x]

parseExpr :: Parser LispVal
parseExpr = parseAtom
        <|> parseString
        <|> parseNumber
        <|> parseQuoted
        <|> do char '('
               x <- try parseList <|> parseDottedList
               char ')'
               return x

showVal :: LispVal -> String
showVal (String contents) = "\"" ++ contents ++ "\""
showVal (Atom name) = name
showVal (Number contents) = show contents
showVal (Bool True) = "#t"
showVal (Bool False) = "#f"
showVal (List contents) = "(" ++ unwordsList contents ++ ")"
showVal (DottedList head tail) = "(" ++ unwordsList head ++ " . " ++ showVal tail ++ ")"

unwordsList :: [LispVal] -> String
unwordsList = unwords . map showVal

readExpr :: String -> String
readExpr input = case parse parseExpr "lisp" input of
    Left err -> "No match: " ++ show err
    Right val -> "Found " ++ show val

main :: IO ()
main = do args <- getArgs
          putStrLn (readExpr (args !! 0))

裏・雅山流

ヨコゼキにて。

香りもいいし、酸と爽やかさのバランスもいい。飲み飽きしないしオススメ。あればまた買う。

1340524857

今日の畑(120623)

先日の台風4号のせいで支柱が倒れたり、ズッキーニがおかしな方向向いてた。

キュウリは1本収穫した。

1340524418

サツマイモの苗は順調。枝豆とインゲンもまぁまぁ。

1340524420

ズッキーニは折れた葉を処分して支柱で支えるようにした。

1340524422

トマトはちょっと赤くなってた。

1340524424

ベランダで出番を待っている空芯菜とモロヘイヤ。再来週くらいに定植かな。

1340524425

収穫したもの

  • ズッキーニ
  • キュウリ
  • シソ
  • 人参

ParsecでつくるJSON Parser (RWH 16章)

HaskellでChemistryやBiology関連のParser書きたいなぁと思っているのでParsecをきちんと使えるようになりたい。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan
オライリージャパン / 3990円 ( 2009-10-26 )


16章にJSONのParserを書く例が載っていたのでやってみた。写経しててみて、Parserとデータコンストラクタというかデータ定義はセットなのかな?と思ったんだけどどうなんだろう?

Hackageに参考になりそうなパッケージないか、あとで探す。

{-# LANGUAGE FlexibleInstances,TypeSynonymInstances #-}

import JSONClass
import Numeric
import Control.Applicative
import Control.Monad (MonadPlus(..), ap)
import Text.ParserCombinators.Parsec hiding (many, optional, (<|>))

p_text :: CharParser () JValue
p_text = spaces *> text <?> "JSON text"
    where text = JObject <$> p_object
             <|> JArray <$> p_array

p_series :: Char -> CharParser () a -> Char -> CharParser () [a]
p_series left parser right =
    between (char left <* spaces) (char right) $
            (parser <* spaces) `sepBy` (char ',' <* spaces)

p_array :: CharParser () (JAry JValue)
p_array = JAry <$> p_series '[' p_value ']'

p_object :: CharParser () (JObj JValue)
p_object = JObj <$> p_series '{' p_field '}'
    where p_field = (,) <$> (p_string <* char ':' <* spaces) <*> p_value

p_value :: CharParser () JValue
p_value = value <* spaces
    where value = JString <$> p_string
              <|> JNumber <$> p_number
              <|> JObject <$> p_object
              <|> JArray <$> p_array
              <|> JBool <$> p_bool
              <|> JNull <$ string "null"
              <?> "JSON value"

p_bool :: CharParser () Bool
p_bool = True <$ string "true" <|> False <$ string "false"

p_value_choice = value <* spaces
    where value = choice [ JString <$> p_string
                         , JNumber <$> p_number
                         , JObject <$> p_object
                         , JArray <$> p_array
                         , JBool <$> p_bool
                         , JNull <$  string "null"
                         ]
                  <?> "JSON value"

p_number :: CharParser () Double
p_number = do s <- getInput
              case readSigned readFloat s of
                [(n, s')] -> n <$ setInput s'
                _ -> empty

p_string :: CharParser () String
p_string = between (char '\"') (char '\"') (many jchar)
    where jchar = char '\\' *> (p_escape <|> p_unicode)
              <|> satisfy (`notElem` "\"\\")

p_escape = choice (zipWith decode "bnfrt\\\"/" "\b\n\f\r\t\\\"/")
    where decode c r = r <$ char c

p_unicode :: CharParser () Char
p_unicode = char 'u' *> (decode <$> count 4 hexDigit)
    where decode x = toEnum code
              where ((code,_):_) = readHex x

動かす

*Main> parse p_text "(unknown)" "[1,2,3]"
Right (JArray (JAry {fromJAry = [JNumber 1.0,JNumber 2.0,JNumber 3.0]}))
*Main> parse p_text "(unknown)" "{\"test\":3}"
Right (JObject (JObj {fromJObj = [("test",JNumber 3.0)]}))

ちゃんと動いた。JSONの例題は5,6,16章ととびとびになっているので全体像が掴みにくかった。

JSON型クラスを作ってみた (RWH 6章)

こんな感じで定義してあるので

instance (JSON a) => JSON [a] where
    toJValue = undefined
    fromJValue = undefined

instance (JSON a) => JSON [(String, a)] where
    toJValue = undefined
    fromJValue = undefined

instance (JSON a) => JSON (JObj a) where
    toJValue = JObject . JObj . map (second toJValue) . fromJObj
    fromJValue (JObject (JObj o)) = whenRight JObj (mapEithers unwrap o)
        where unwrap (k,v) = whenRight ((,) k) (fromJValue v)
    fromJValue _ = Left "not a JSON object"

instance (JSON a) => JSON (JAry a) where
    toJValue = jaryToJValue
    fromJValue = jaryFromJValue

newtype JAry a = JAry {
      fromJAry :: [a]
    } deriving (Eq, Ord, Show)

newtype JObj a = JObj {
      fromJObj :: [(String, a)]
    } deriving (Eq, Ord, Show)

アレイとオブジェクトをJValue型にするために

*JSONClass> toJValue . JObj $ [("test", 1)]
JObject (JObj {fromJObj = [("test",JNumber 1.0)]})
*JSONClass> toJValue . JAry $ [1,2,3]
JArray (JAry {fromJAry = [JNumber 1.0,JNumber 2.0,JNumber 3.0]})

ってやらないといけないのが、ちょっと引っかかる。動かすとこまでは書いてないからなぁ。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan
オライリージャパン / 3990円 ( 2009-10-26 )


ソーシャルメディア進化論

amazonでは評価が分かれているが、物語としてみればそれなりに面白かった。数学的な話は殆ど出てこないし、ネットワークモデルとかかじったことがあれば、数学的になんかおかしい理論展開だなぁと思うけど。

ProductName ソーシャルメディア進化論
武田隆
ダイヤモンド社 / 1890円 ( 2011-07-29 )


ソーシャルネットワークとマネタイズを如何に両立させるかっていうあたりを拾い読みすればいいんじゃないかな。 コミュニティデザインのあたりは参考になることがあるんじゃないかなぁ。

改めて読みなおしてみると確かに自社宣伝臭はちょっと強いかもと思った。

  • 繭化の問題に対する処方箋は、実名性を高める方向ではなく、匿名性を維持したまま、社会につながるモデルであるということになる。
  • Facebookのように実名性を高め、オープンを強要すれば本音は隠れてしまう。

Haskellで(,)が関数だと知ったのだが中置演算子として使えないの?

RWHを読んでいたら(,)が関数だと知った

Prelude> :t (,)
(,) :: a -> b -> (a, b)
Prelude> (,) 'a' 'b'
('a','b')

期待通りタプルを返す。で括弧でくくられているってことは中置記法使えるのかなと思いやってみた

Prelude> 'a' , 'b'

<interactive>:33:5: parse error on input `,'

これはparse error。

なんで?

2012.06.21 追記

@ksmakotoに(,)はデータコンストラクタじゃないかと指摘された。

Prelude> :i (,)
data (,) a b = (,) a b  -- Defined in `GHC.Tuple'
instance (Bounded a, Bounded b) => Bounded (a, b)
  -- Defined in `GHC.Enum'
instance (Eq a, Eq b) => Eq (a, b) -- Defined in `GHC.Classes'
instance (Ord a, Ord b) => Ord (a, b) -- Defined in `GHC.Classes'
instance (Read a, Read b) => Read (a, b) -- Defined in `GHC.Read'
instance (Show a, Show b) => Show (a, b) -- Defined in `GHC.Show'

確かにデータコンストラクタですね。だから中置記法が使えないってことでいいのかな?

ついでに文法への参照も教えてもらったので後で読んでみる。

ProductName Real World Haskell―実戦で学ぶ関数型言語プログラミング
Bryan O'Sullivan
オライリージャパン / 3990円 ( 2009-10-26 )