Skip to content

Instantly share code, notes, and snippets.

@mzp
Last active December 17, 2015 23:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mzp/5692680 to your computer and use it in GitHub Desktop.
Save mzp/5692680 to your computer and use it in GitHub Desktop.

Functional Ninja #2 (OCaml編)

Functional Ninjaとは

ニンジャー

事前準備

コンパイラのインストール

Ubuntu/Debian ユーザ

$ sudo aptitude install curl m4 build-essential libcurl4-openssl-dev
$ curl -kL https://raw.github.com/hcarty/ocamlbrew/master/ocamlbrew-install | bash

インストール後に表示されるメッセージに従い、以下の行を~/.bashrcに追記する

source ~/ocamlbrew/ocaml-4.00.1/etc/ocamlbrew.bashrc

MacOS X ユーザ

Homebrewユーザ

$ brew install opam

MacPortユーザ

$ port install opam

その後、OPAMを初期化します。

$ opam init

Windows ユーザ

  1. VirtualBox をインストールする
  2. Ubuntu をインストールする
  3. "Ubuntu/Debian ユーザ" の節と同じ

その他

以下のものがインストールされている状態にしてください。

ライブラリのインストール

$ opam install ocamltter
$ opam install utop
$ opam install oasis

当日やること

OCamlとは

  • 静的型付け関数型言語
  • 非純粋なので破壊的操作も書ける
  • 比較的高速に動作する

インストール作業(つづき)

ホームディレクトリに以下のような .ocamlinit を作ります。 4行目は、自分のホームディレクトリに読み替えてください。

let interactive = !Sys.interactive;;
Sys.interactive := false;; (*Pretend to be in non-interactive mode*)
(* 自分のホームディレクトリに書き換える。 ~は使えない:( *)
#use "/home/mzp/.opam/system/lib/toplevel/topfind";;
Sys.interactive := interactive;; (*Return to regular interactive mode*)

準備

ディレクトリを作る。

$ mkdir ninja_bot
$ cd ninja_bot
$ git init

_oasis というファイルを作る。 直接以下の内容を書いてもいいし、 oasis quickstart で対話的に作ってもいい。

OASISFormat: 0.3
Name:        ninja_bot
Version:     1.00
Synopsis:    ninja
Authors:     mzp
License:     GPL-3.0
Plugins:     META (0.3), StdFiles (0.3), DevFiles (0.3)

Executable ninja
  Path:       src
  BuildTools: ocamlbuild
  MainIs:     ninja.ml

簡単なhello worldを書いてみる。

$ mkdir src
$ echo 'let _ = print_endline "hello, world!"' > src/ninja.ml

ビルドする。

$ oasis setup
$ make
$ ./ninja.byte
hello, world!

この辺で一回コミットしておきましょう。

OAuth認証の準備

OCamltterのコードを流用するため、いくつか魔術的なことをします。

$ curl https://raw.github.com/mzp/ninja_bot/master/src/minOCamltter.ml > src/minOCamltter.ml

_oasis を以下のようにします。

OASISFormat: 0.3
Name: ninja_bot
Version: 1.00
Synopsis: ninja
Authors: mzp
License: GPL-3.0
Plugins: META (0.3), StdFiles (0.3), DevFiles (0.3)

BuildTools: ocamlbuild, camlp4

Executable ninja
  Path: src
  BuildTools: ocamlbuild
  MainIs: ninja.ml
  Builddepends: twitter, meta_conv.syntax, ocaml_conv, camlp4
  ByteOpt: -w +a -warn-error +a -annot

_tags の一番下に追記します。

<src/*.ml{,i}>: syntax_camlp4o

ninja.ml を以下のように変更し、MinOCamltterへの依存関係を追加します。

let _ = MinOCamltter.get_oauth

そしてビルドします。

$ oasis setup
$ make

対話環境で遊ぶ

では対話環境で遊びましょう。

$ rlwrap ocaml # もしくは utop
# #require "twitter";;
# #directory "_build/src";;
# #load "minOCamltter.cmo";;

# let o = MinOCamltter.get_oauth ()
val o : Twitter.Oauth.t =
  {Twitter.Oauth.consumer_key = "vS0nKAS6ieWL76zZaQgF4A";
   Twitter.Oauth.consumer_secret = "XHa1ZiPcNRsYKw4mdIv8wHUiNulpBFxKT1ntXXuJgo";
   Twitter.Oauth.access_token = "-"
   Twitter.Oauth.access_token_secret = "-"
   Twitter.Oauth.verif = "-"}

(* 発言してみる *)
# Twitter.Api11.Tweets.update o "#functionalNinja hello";;
- : [> `Error of [> `Http of int * string | `Json of Twitter.Api_intf.Json.t Meta_conv.Error.t ]
    | `Ok of Twitter.Api_intf.Tweet.t ]
= `Ok <obj>

(* 検索したいな *)
#  Twitter.Api11.Search.tweets;;
- : ?count:int ->
?since_id:int64 ->
Twitter.Oauth.t ->
string ->
[> `Error of [> `Http of int * string | `Json of Twitter.Api_intf.Json.t Meta_conv.Error.t ]
 | `Ok of Twitter.Api_intf.Search_tweets.t ]
 = <fun>

(* oauthと文字列をわたせばいいのか。 たぶん検索キーワードだな *)
# let xs = Twitter.Api11.Search.tweets o "#functionalNinja";;
val xs :
[> `Error of [> `Http of int * string | `Json of Twitter.Api_intf.Json.t Meta_conv.Error.t ]
| `Ok of Twitter.Api_intf.Search_tweets.t ] =
`Ok <obj>

(* `Okの中身を取得する *)
# let ys = match xs with `Ok ok -> ok | _ -> failwith "oops"
val ys : Twitter.Api_intf.Search_tweets.t = <obj>

(* Twitter.Api_inf.Search_tweets.t ってなんだろう *)
# module M  = Twitter.Api_intf.Search_tweets;;
module M :
sig
  module Search_metadata :
    sig
      type t =
        < completed_in : float; count : int; max_id : int64; next_results :
          string; query : string; refresh_url : string; since_id :
          int64; unknowns : Twitter.Api_intf.Json.t Twitter.Api_intf.mc_leftovers >
      val json_of_t : t -> Twitter.Api_intf.Json.t
      val t_of_json : (t, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t
      val t_of_json_exn : (t, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t_exn
      val ocaml_of_t : t -> Ocaml.t
      val t_of_ocaml : (t, Ocaml.t) Meta_conv.Types.Decoder.t
      val t_of_ocaml_exn : (t, Ocaml.t) Meta_conv.Types.Decoder.t_exn
    end
  type t = < statuses : Twitter.Api_intf.Tweet.t list; unknowns : Twitter.Api_intf.Json.t Twitter.Api_intf.mc_leftovers >
  val json_of_t : t -> Twitter.Api_intf.Json.t
  val t_of_json : (t, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t
  val t_of_json_exn : (t, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t_exn
  val ocaml_of_t : t -> Ocaml.t
  val t_of_ocaml : (t, Ocaml.t) Meta_conv.Types.Decoder.t
  val t_of_ocaml_exn : (t, Ocaml.t) Meta_conv.Types.Decoder.t_exn
  val format : Format.formatter -> t -> unit
end
(* ふむ, statusesを持つオブジェクトなのか *)
# let zs = ys#statuses;;
val zs : Twitter.Api_intf.Tweet.t list =
[<obj>; <obj>; <obj>; ]

(* Twitter.Api_intf.Tweet.t ってなんだろう *)
# module M = Twitter.Api_intf.Tweet;;
module M :
 sig
   type t =
       < contributors : Twitter.Api_intf.Json.t option; coordinates :
         Twitter.Api_intf.Json.t option; created_at : Twitter.Api_intf.Time.t;
         entities : Twitter.Api_intf.Entities.t Meta_conv.Open.mc_option; favorited :
         bool; geo : Twitter.Api_intf.Json.t option; id : int64; in_reply_to_screen_name :
         string option; in_reply_to_status_id : int64 option; in_reply_to_user_id :
         int64 option; place : Twitter.Api_intf.Json.t option; possibly_sensitive :
         bool Meta_conv.Open.mc_option; retweet_count : int; retweeted :
         bool; retweeted_status : t Meta_conv.Open.mc_option; source :
         Twitter.Api_intf.Client.t; text : Twitter.Api_intf.Text.t; truncated :
         bool; unknowns : Twitter.Api_intf.Json.t Twitter.Api_intf.mc_leftovers; user :
         Twitter.Api_intf.User.t >
   val json_of_t : t -> Twitter.Api_intf.Json.t
   val t_of_json : (t, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t
   val t_of_json_exn : (t, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t_exn
   val ocaml_of_t : t -> Ocaml_conv.target
   val t_of_ocaml : (t, Ocaml_conv.target) Meta_conv.Types.Decoder.t
   val t_of_ocaml_exn : (t, Ocaml.t) Meta_conv.Types.Decoder.t_exn
   type ts = t list
   val json_of_ts : ts -> Twitter.Api_intf.Json.t
   val ts_of_json : (ts, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t
   val ts_of_json_exn : (ts, Twitter.Api_intf.Json.t) Meta_conv.Types.Decoder.t_exn
   val ocaml_of_ts : ts -> Ocaml.t
   val ts_of_ocaml : (ts, Ocaml.t) Meta_conv.Types.Decoder.t
   val ts_of_ocaml_exn : (ts, Ocaml.t) Meta_conv.Types.Decoder.t_exn
   val format : Format.formatter -> t -> unit
   val format_ts : Format.formatter -> ts -> unit
 end

(* とりあえずtextとかもってるのか。出力してみるか *)
# List.iter (fun z -> print_endline z#text) zs;;
.....
.....
....

(* ふぁぼりたい *)
# Twitter.Api11.Favorites.create;;
- : ?include_entities:bool ->
Twitter.Oauth.t ->
int64 ->
[> `Error of [> `Http of int * string | `Json of Twitter.Api_intf.Json.t Meta_conv.Error.t ]
 | `Ok of Twitter.Api_intf.Tweet.t ]
 = <fun>

(* ふむIDがいるのか *)
# (List.hd zs)#id;;
- : int64 = 342261499638718464L

(* ふぁぼる *)
# Twitter.Api11.Favorites.create o (List.hd zs)#id;;
- : [> `Error of [> `Http of int * string | `Json of Twitter.Api_intf.Json.t Meta_conv.Error.t ]
 | `Ok of Twitter.Api_intf.Tweet.t ]
 = `Ok <obj>

れっつこーでぃんぐ

だいたい挙動がわかったので、「#functinalNinja」とつぶやいている人をふぁぼりまくってみましょう。

解答例: https://github.com/mzp/ninja_bot

参考文献


Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment