技術書典 4 にサークル参加した

4月22日に開催された『技術書典 4』にサークル参加してきました。

techbookfest.org

技術書典は1と2に個人で参加して3は会社として参加して今回の4は会社と個人と2サークルにかかわっていました。

超技術書典を抜くと皆勤です。

個人サークル

既刊として『遠回りして学ぶ Yesod 入門』を増刷し、新刊は『手続き Haskell』を持っていきました。

doujin.kakkun61.com

doujin.kakkun61.com

数字

売り上げ部数は、どんぶり勘定ですが(特に Yesod 本のダウンロードカードが完全に記憶による)下記の通りとなりました。

  • Yesod 本
    • 紙 + PDF
      • 30
    • PDF
      • 4
  • 手続き Haskell
    • 紙 + PDF
      • 60
    • PDF
      • 9

60部は次回イベント頒布も込みでの印刷のつもりでしたが、技術書典自体の来場者が倍になったことで全部捌けてしまいました。

両方とも紙の本はイベント終了の1時間半前から1時間前ぐらいに完売し、あと5冊ずつぐらいあればちょうどよかったかなという感じでした。

どちらも紙 + PDF が1000円、PDF のみが800円でした。正直『手続き Haskell』は30ページないのでちょっと高いかなと思いつつ会計の簡潔さのために切り上げました。値段で買うか悩む人がいなかったわけではないですが、ほとんどの人は、前から思っていましたが、買うか決めた後に値段の確認をするようです。

あと、16時ぐらいまで入場規制がかかっていたせいでポツポツと最後まで買ってくれる人がいて、それは嬉しいんですが、それに加えて会場の混みぐあいとで自分が買いに回ることが全然できませんでした。次も晴れたら、自分と売り子、買い出しの3人で回そう……

支払い方法は下記の通りです。

  • 現金
    • 多数
  • かんたん後払い
    • 6
  • Square(クレジットカード)
    • 0

Square 決済、実は1度もしたことがないので待ってます。(技術書典2のときに1度チャレンジしてくれた人がいたんですがうまく読めなかったことはあります。)

書籍について

新刊の『手続き Haskell』についてブログで説明していなかったのでしておきます。

ちなみに Yesod 本は表紙にポリプロピレンが貼られちょっとだけ品質アップしています。

手続き Haskell

タイトルでネタ本と思われることがあったのですが、大まじめに書いています。

ネタだと思われるという状況を変えないといけないと思っていて、というのも Haskell で手続きを書くのは全くめずらしいものではないです。なぜならそのためにモナド用の do 構文をサポートしているのですから。

世間では Haskell関数プログラミングをしないといけないという強迫観念のようなものが強いと思います。

Haskell では普通に手続きが書けるのだということが伝われた執筆したカイがあったというものです。

本書にはもう1つ執筆の理由があって、それは入門者によくある「読めるけど書けない」問題の解消です。

入門書を読んで書いてあることは分かるけど Haskell で書こうとすると書けないということが多いように思います。それは、元々手続きプログラミングをやっている人の場合、まず頭に思い浮かぶアルゴリズムが手続き的だからです。なので、それをそのまま Haskell で書けるノウハウを提示したつもりです。

また、Haskell の文法は C 族の言語や PythonRuby などとはかなり異なります。その上、関数プログラミングをするとなると、入門者は二重に壁があることになります。そこで、関数プログラミングを後回しにすることで、文法を身につけることが容易にすることも狙いです。

とはいえ Haskell らしいソースコードがあるにはあるので最後の章で、両方の書き方を対比しています。

本文中に書き忘れたのですがソースコードGitHub で公開していますので実際に動かしてみたい方はクローンしていじってみてください。

github.com

執筆

今回に合わせて同人活動をまとめたサイトを GitHub Pages で作ったのですが、せっかく Haskell のサークルなので Hakyll を使うように書き換えようと思ったのが時間が溶ける原因でした。

まずテンプレートエンジンが気に入らなく Jekyll と同じ Liquid を使おうとしたら、ライブラリーが完全でなく、まずそれの自作から始めてしまい、まさにヤクの毛刈りですね。

さすがにこのままだと執筆時間がなくなってしまうと、1ヶ月ほど前から執筆を始めました。

時間がすでになくなっているのに、執筆を始めると想定より内容が膨らみ始め、当初は Haskell の文法も解説するつもりでしたが、入門書の副読書と目標を変えることで、なんとか収まりのよい形になったのはよかったと思います。

ただそれでも自己レビューが不十分だなと刷り上がった本を読んで思えたのは反省点です。

執筆で今回よかったのはプリンターを買ったことでした。人にもよるでしょうが、自分の場合は自己レビューする場合、紙で赤ペンを入れていかないとまちがいが見つけられません。これまでは外で刷っていたのですがもう今回思い切ってプリンターを買いました。

プリンターの知識が10年ほど止まっていたので知らなかったのですが、今時はロースペック機以外はスキャナーが付くのですね。

そこでせっかくなので今回の表紙は手書き文字を取り込んでみました。それに合わせて表紙用紙を軽く波打った紙にしてみたのですが、これはよかったと思います。手書き文字とよくマッチしています。

オンライン販売

これまで通り BOOTH でオンライン販売しておりますのでよろしくお願いします。

追記(2018.09.12):『遠回りして学ぶ Yesod 入門』は商業化しましたのでそちらでお買い求めください。詳細はこちら

kakkun61.booth.pm

kakkun61.hatenablog.com

会社サークル

会社としては技術書典 3 からの2回目のサークル参加となります。

techbookfest.org

今回は個人サークルもあるので、会社の方は編集に専念しました。

前回は完全に有志でプライベートや10%プロジェクト時間にやっていたのですが、イベントの後に人事部の方からアプローチがあり、今回は業務として執筆できるようになりました。ありがたい。

また今回は表紙・ダウンロードカードを社内の絵描きの人たちに描いてもらいました。前回はクールな感じにしあがったのですが今回はキュートになりました。大きいポスターも作りました。ポスターがあると大手壁サークル感がありますね。

その他の詳細は会社ブログで公開予定ですのでもうしばらくお待ちください。

技術書典 5

次回がどうなるのか分かりませんが、『俺々言語にだって型推論が欲しい!』という題で書いてみようかなぁと思っています。(大いに変わる可能性がある。)

なぜ Haskell が好きなのか

自分は Haskell が好きで休日は Haskell を書いています。そういうことを言うと関数型が好きなんですねと言われるのですが、Haskell のよさはそこじゃないと感じているので書き起こそうかと、筆を執りました。

というわけで、この記事は技術的文書というよりもお話です。Haskell を知らない人向けです。

この記事は Haskell Advent Calendar 2017 その3の6日めの記事です。6日が過ぎても担当のいない日だったため担当します。

関数型プログラミングだから Haskell が好きというわけではない

まず、「Haskell というと手続き型とは全然違う関数型なんでしょう?」という印象を持つかと思いますが、一部合っていて一部まちがっていると思っています。

まず、用語の意味を絞っておかないと議論が発散してしまうのでそうしておきます。

ここでは「手続き型」は、先に書いた処理が後に書いた処理に影響を与えるようなプログラムを指すものとします(先や後は、ファイルの先頭に近い方が先、反対が後とします)。C・C++Ruby などなどよくあるプログラミング言語です。

ここでは「関数型」は、関数がファーストクラスであることが最低条件とします。つまり、Func<T, …, T>インスタンスがある C# は満たしていますし、JavaScriptPHP もそうです。あくまで最低条件で実際にはそれらを活用したライブラリーがないと強くはそう言わないと思います。

さて、これら用語でいうと Haskell は「関数型」ですし、ライブラリーのサポートもあるので強くそうです。では、「手続き型」かというと意外に思うかもしれませんが、手軽に手続きも書けます。Web アプリケーションプログラミングなどではバリバリ手続きを書きます。

なので最初の「Haskell というと手続き型とは全然違う関数型なんでしょう?」に対する答としては「Haskell は手続き型であるし、関数型でもあり、それらは全然違うわけではない。」となります。

データの設計がしやすい

Haskell では「代数的データ型」(algebraic data type)を採用しています。代数的データ型は以降 ADT と表記します。代数的データ型は、直和と直積を持つ系です。(Haxe・Rust・Scala・Kotlin・Swift なども有していると聞いています。)

まず、直積について見ていきます。例えば、よくある例で Person という型があって nameage というフィールドがあるとすると、それは Haskell では次のように記述できます。

data Person =
    Person
        { name :: String
        , age :: Int
        }

C の構造体や C#Java のクラスと同じですね。

値を作るときは次のようになります。

me = Person { name = "Kazuki Okamoto", age = 28 }

次は直和についてです。先の Parson の例の続きで、実は乗客として人を扱うために Parson を作ったとしましょう。さて、乗客からの要望により新たにペットも乗せられるようになりました。「乗客」型は、値として「人」もしくは「動物」ということになります。これを Haskell で記述すると次のように記述します。

data Passenger =
    Person
        { name :: String
        , age :: Int
        }
    | Animal
        { species :: String
        , name :: String
        }

値を作るときは次のようになります。

me = Person { name = "Kazuki Okamoto", age = 28 }
pet = Animal { species = "dog", name = "Max" }

同等のことを C#Java などでやろうとすると、インタフェース1つとクラスが2つ必要になります。別の相違点として、直和型の場合は乗客は人もしくは動物のみであることは定義から分かりますが、継承でシミュレートした場合はプログラム全体を検索しないと他の種の乗客があるかどうか分かりません。反対に、継承を使った場合は元のコードに手を加えずに拡張ができるということがいえます。

型の定義部でしか値の種類の追加ができないことは、パターンマッチにおいて網羅性チェックができるという利点があります。

case me of
    Person n a -> {- 何らかの処理 -}
    Animal s n -> {- 何らかの処理 -}
-- 例えば 上記の Animal のパターンがなければ次のような警告が出ます
-- warning: [-Wincomplete-patterns]
--    Pattern match(es) are non-exhaustive
--    In a case alternative: Patterns not matched: Animal

Haskell における手続き

手続きを使ったプログラミングの例として、西暦を入力すると平成何年かを出力するようにしましょう。

import System.IO (hFlush, stdout)

main :: IO ()
main = do
    putStr "Christian Era: "
    hFlush stdout
    christianEra <- readLn :: IO Int
    let heiseiEra = christianEra - 1988
    putStr "Heisei Era: "
    putStrLn (show heiseiEra)

実行すると次のようになります。

λ stack runghc .\heisei.hs
Christian Era: 2017
Heisei Era: 29

C 系との違いとしては、関数呼び出し(関数適用)が括弧なしに関数と引数を並べる点、変数への代入(変数の束縛)が … <- …let … = … の2種類を使い分ける点があり、それを除けば雰囲気で読めるのではないかと思います。

先の例では main の定義の先頭に do キーワードが使われていますが、Haskell では do キーワードを使えば、その式を手続きで書けるようになります。

詳細は省きますが for_ 関数を使えばループを書けますし、IORef などを使えば再代入(再束縛)可能な変数も作れます。

副作用の明示

Haskell で手続きが難なく書けることは示しましたが、ここで Haskell でいいことがあります。それは副作用の種類が型に明示されるということです。

先の例の mainIO () という型になっています。これは IO を副作用として持つことを意味します。他には、Reader r a というものは、読み取り専用の大域変数(型は r)があることを意味します。ST s a は読み書きのできる大域変数があることを意味します。もちろん大域とはいいつつプログラム全体ではなく副作用は局所化されます。(なんか矛盾してるようですけど局所的に大域的なのです。)などなど、アプリ固有の副作用を示す型を作ることもできます。

IO の手続きの中から IO の手続きは呼べますし副作用のない関数も呼べますが、反対に副作用のない関数の中から IO の手続きは呼べません。呼べてしまうと関数に副作用ができてしまうので当然ですね。STST の手続きの中から ST の手続きと副作用のない関数が呼べます。副作用のない関数の中からは ST の手続きを「実行」することができます。副作用は ST の中にのみ影響するので ST 全体を実行することには副作用がありません。ST の中に局所化される副作用しか ST の中には書けないので入出力などは ST の中に書けません。

これは、プログラムのまちがいが起こりにくいのは当然、他人のプログラムを読むときも大変有用です。C#Java などのオブジェクト指向では手続きのカプセル化には成功しましたが、副作用の有無についても隠蔽してしまいました。

せめて pure 修飾子などがあるとよいですね。一番近いのは IntelliJ IDEA で Java を書いたときに付けられる @Contract(pure = true) ですかね。

まとめ

以上、なぜ自分が Haskell が好きなのかというと次が主要な理由です。

  • 代数的データ型、特に直和型があることはモデルの設計が簡単になる
  • Haskell で手続きは書きやすいし、実際よく書く
  • 副作用が明示されることは、コードのまちがいが減るし、読みやすい

C# でランク N 多相

追記:これだと解決できていない。

C#Sprache というパーサーコンビネーターがあるのだが、最近そいつを継続渡しスタイル(continuation passing style; CPS)にしてやろうと、Haskell のパーサーコンビネータattoparsec を参考にいじっていた。

そこでこういう型があった。

newtype Parser i a = Parser {
      runParser :: forall r.
                   State i -> Pos -> More
                -> Failure i (State i)   r
                -> Success i (State i) a r
                -> IResult i r
    }

この型単品では C# への翻訳で困らないのだが、次のような関数があると困ったことになる。

plus :: Parser i a -> Parser i a -> Parser i a

そこで StackOverflow で質問してなるほどなーと思ったのでまとめてみる。

newtype は実質型の別名なので(明示的な相互変換が要るが)、plus 型は次のように理解できるわけだ。

plus :: (forall r.
            State i -> Pos -> More
         -> Failure i (State i)   r
         -> Success i (State i) a r
         -> IResult i r)
     -> (forall r.
            State i -> Pos -> More
         -> Failure i (State i)   r
         -> Success i (State i) a r
         -> IResult i r)
     -> (forall r.
            State i -> Pos -> More
         -> Failure i (State i)   r
         -> Success i (State i) a r
         -> IResult i r)

ややこしいので今回の話題に本質的でない部分を省くと、次のようになる。(意味のあるものではなくなっているが。)

f :: (forall r. r -> r)
  -> ()

この型は C# でいうところの void F<R>(Func<R, R> f) なのではないかと思うかもしれないが次のような場合に問題がある。

static void Main(string[] args)
{
    F(Id);
}

T Id<T>(T t) { return t; }

void F<T>(Func<T, T> f) { // This is not sound!
    System.Console.WriteLine("{0}", f(1));
    System.Console.WriteLine("{0}", f("one"));
}

F<T>(Func<T, T>) を使うところで推論できないとエラーが出、じゃあと T の型を指定しようとすると int にしても string にしても問題があるわけだ。

f の型は F を適用するところでは一意に決めず多相のままにしておきたいわけだ。こういうのをランク 2 多相という。(上述の F はランク 1 多相。)

で、C# はそんな多相をサポートしてないしなんとか回避策がないものかと考えてパッっと出てこなかったんで StackOverflow に聞いてみたら次のような回答が得られた。

using System;

interface IGenericSameTypeFunction
{
    T Apply<T>(T input);
}

public class SimpleIdentityFunction : IGenericSameTypeFunction
{
    public T Apply<T>(T input) => input;
}

class Test
{    
    static void F(IGenericSameTypeFunction function)
    {
        Console.WriteLine(function.Apply(1));
        Console.WriteLine(function.Apply("one"));
    }

    static void Main()
    {
        F(new SimpleIdentityFunction());
    }
}

なるほどなぁ。部分型多相を使うことでランク N 多相を実現するのだな。

“meaningless REX prefix used” “use of REX.w is meaningless”

コマンドプロンプトPowerShell でプログラムを実行したときに次のようなエラーが出て困っていた。

[0x7FFE822C2B00] ANOMALY : meaningless REX prefix used
[0x7FFE85B3DDA0] ANOMALY : use of REX.w is meaningless (default operand size is 64)

REX プレフィックスとは何ぞや。

ロングモードでは、64ビット化するにあたり、16ビット時代から存在した1バイトのinc/dec命令であるinc reg16/dec reg16命令(op 0x40〜0x4f)を廃止してこの位置にプリフィックスを再定義した。結果として、4ビット分のビットフィールドが確保された。 http://www.wdic.org/w/SCI/REX%E3%83%97%E3%83%AA%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E3%82%B9

なんか CPU 的に意味のないフラグが立ってるっぽいけど、一体なんでなのやら。

stack が ghc-pkg 辺りの出力をパースをしてるらしく、この出力のせいでエラーになってずっと困ってた。

とりあえずググってみたけど、判然とした原因はいまいちなく、ディスプレードライバーが原因やらウイルス対策ソフトが原因やらという感じだったので、試しに導入していたウイルスセキュリティを無効にして Windows Defender を有効にしてみた。

そうしたらメッセージが出なくなった。原理はよく分かってないけど、一事例として。

関係と代数的データ型との相互変換についての妄想 その3

思い付き その2

class QueryUser ? where
    queryUser :: (?) => Key -> MonadDB User

instance QueryUser ? where
    -- queryUser :: (HasFriends ?) => Key -> MonadDB User
    query =instance QueryUser ? where
    -- queryUser :: (HasName ?) => Key -> MonadDB User
    query = …

userFriends :: (HasFriends ?) => User -> [User]
userName :: (HasName ?) => User -> [User]

ふむー?userFriendsuserName を使った項は、まぁ (HasFriends ?, HasName ?) になりそうな気はするな。HasFriends ?? に入る型と対応する値が要る気がする。

そもそもそれぞれでインスタンス実装できるのか?

ConstraintKinds?

“When matching, GHC takes no account of the context of the instance declaration (context1 etc).” ってあるしムリっぽい。 https://downloads.haskell.org/~ghc/7.0.1/docs/html/users_guide/type-class-extensions.html#instance-overlap

GHC 8.0 だとその記述なくなってるな。 https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#overlapping-instances

とはいえムリっぽいのは変わりなさそうなんで、次は普通にモナドの方針で行ってみるか。

関係と代数的データ型との相互変換についての妄想 その2

思い付き その1

型レベルのリストをクエリー函数に与えてやる。(DataKinds が有効)

data Attr = Id | Name | Friends
data User = User { id :: Int, name :: String, friends :: [User] }
query :: Proxy '[??] -> Key -> MonadDB User
userFriends :: Proxy '[Friends] -> User -> [User]

リストだとダメだなーって気付いた。勝手に型の和が取られるわけじゃないからね。

それはそれとして、分からないところが2点。

1つは上記のコードで query の型の ?? って書いているところをどう書けばよいか。やりたいことは「?? の部分に入るのは 'Attr カインドの任意の型」ということを示したい。

しばらく寝かせて思い付いたけど KindSignatures を有効にして下記でいけるっぽい。

data ProxyAttr (t :: [Attr]) = ProxyAttr

もう1つは、下記のをうまいこと推論してくれないのなんでだ。stack --resolver lts-8.12 exec -- ghci -fprint-explicit-kinds -XDataKinds にて。

後から気付いた ProxyAttr を使うとエラーにならなかった。

ghci> :m + Data.Proxy
ghci> data Attr = Id | Name | Friends
ghci> data User = User { id :: Int, name :: String, friends :: [User] } deriving (Show)
ghci> :{
ghci| userFriends :: Proxy '[Friends] -> User -> [User]
ghci| userFriends _ _ = [User 1 "Ada" []]
ghci| :}
ghci> bob = User 2 "Bob" []
ghci> let p = Proxy in userFriends p bob

<interactive>:9:30: error:
    • Couldn't match type*’ with ‘[Attr]’
      Expected type: Proxy [Attr] ((':) Attr 'Friends ('[] Attr))
        Actual type: Proxy * ((':) Attr 'Friends ('[] Attr))
    • In the first argument of ‘userFriends’, namely ‘p’
      In the expression: userFriends p bob
      In the expression: let p = Proxy in userFriends p bob

Proxy に型注釈してやると大丈夫。

ghci> let p = Proxy :: Proxy '[Friends] in userFriends p bob
[User {id = 1, name = "Ada", friends = []}]

どっちにしろリストの戦略は破綻してるのでボツ。

制約の方に情報を乗せないとあかん気がしている。