もう6年も記事を書いてないんだなぁ

最近は何でもかんでもやる人になってて、技術としてこれ!ってのがない。
昔はプログラミング言語にこだわってましたが、最近は少し勉強すればどれも一緒という感じで深く踏み入ってないところがある。
深く踏み入れば実際には言語でも得手不得手が違うのと、それぞれ言語の仕組みとしての違いは面白いんだけどそれだとお金貰えないのでつらい。
なんでも拾って自分でやってしまった結果全部浅く広くになって来たので、ここいらで何か集中できるものを探したいし、それができる環境がほしい。

clang3.2(trunk) on MinGW(MSYS) with GCC 4.7.0

clangのコンパイル方法は日本語でもあちこちにあるので省略。
現時点のsvn trunkからcheak outしたソースを使用した場合、
MinGWでGCC4.7.0を使っていると色々と問題が出るのでその修正方法を書きます。

1.Include Pathの解決(既に知っている人が多いと思いますが)
MinGWがc\mingw以外にインストールされている場合Include Pathが解決できずにコンパイルに失敗します。
たとえばMinGWをd\mingwにインストールした場合は下記のように修正します。

clang/lib/Frontend/InitHeaderSearch.cpp
InitHeaderSearch.cpp(line:323)

      AddPath("/mingw/include", System, true, false, false);
#if defined(_WIN32)
      AddPath("c:/mingw/include", System, true, false, false); 
      AddPath("d:/mingw/include", System, true, false, false);  // 追加
#endif

InitHeaderSearch.cpp(line:401)

    // mingw.org C++ include paths
    AddMinGWCPlusPlusIncludePaths("/mingw/lib/gcc", "mingw32", "4.5.2"); //MSYS
#if defined(_WIN32)
    AddMinGWCPlusPlusIncludePaths("d:/MinGW/lib/gcc", "mingw32", "4.7.0");  // 追加
    AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.2");
    AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.6.1");
    AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.2");
    AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.0");
    AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.4.0");
    AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.3.0");
#endif

2.ABI修正
GCC 4.7 changelogに以下の文があります。
Windows x86 targets are using the __thiscall calling convention for C++ class-member functions.
GCC4.7ではメンバ関数コールを__thiscallにする変更が入ってます。これをclang側にも適応します。
clang/lib/AST/ItaniumCXXABI.cpp(line:42)下記を

  CallingConv getDefaultMethodCallConv(bool isVariadic) const {
    return CC_C;
  }

このように修正します。

  CallingConv getDefaultMethodCallConv(bool isVariadic) const {
    if (!isVariadic && Context.getTargetInfo().getTriple().getArch() == llvm::Triple::x86)
      return CC_X86ThisCall;
    else
      return CC_C;
  }

3.memoryヘッダの修正
memoryヘッダをインクルードするとコンパイルエラーになります。
これは4.6.2でも下記のようにエラーになるようです。

c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:228:2: error: no
      matching function for call to '_S_destroy'
        _S_destroy(&_M_mutex);
        ^~~~~~~~~~
c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:273:7: note:
      candidate template ignored: substitution failure [with _Rm =
      __gthread_recursive_mutex_t]: non-type template argument evaluates to 4,
      which cannot be narrowed to type 'bool'
      _S_destroy(_Rm* __mx)
      ^
c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:282:7: note:
      candidate template ignored: substitution failure [with _Rm =
      __gthread_recursive_mutex_t]: no member named 'actual' in
      '__gthread_recursive_mutex_t'
      _S_destroy(_Rm* __mx)
      ^
c:/MinGW/lib/gcc/mingw32/4.6.2/include/c++\ext/concurrence.h:290:7: note:
      candidate template ignored: substitution failure [with _Rm =
      __gthread_recursive_mutex_t]: no type named '__type' in
      '__gnu_cxx::__enable_if'
      _S_destroy(_Rm* __mx)
      ^

これはlibstdc++のバグで、gccのヘッダを修正します。
ext/concurrence.h(line:273)
下記コードを

    template<typename _Rm> 
      static typename __enable_if<sizeof(&_Rm::sema), void>::__type 
      _S_destroy(_Rm* __mx) 

以下のように修正します。

    template<typename _Rm> 
//      static typename __enable_if<sizeof(&_Rm::sema), void>::__type 
      static typename __enable_if<(bool)sizeof(&_Rm::sema), void>::__type 
      _S_destroy(_Rm* __mx) 

参考:[clang-3.2] unique_ptr: compiler errors

Rubyでflip

関数合成できた。ポイントフリーができた。カリー化もある。
なら次はflipで部分適応の順序を入れかえてやりたい。
まずは下記のProc#flipを見て試す。
http://yuroyoro.hatenablog.com/entry/2012/08/10/232443

div = lambda{|x,y| x.to_f / y }
div.(10,2)
#=> 5.0
div.flip.(10,2)
#=> 0.2

確かにできてる。でも3つ引数をとる場合、第2引数と第3引数を部分適応することはできないらしい。
やってみる。

  f = ->a,b,s { s.to_s.gsub(a, b) }
  p f.(/oo/,"aa","foooo")
  #=> "faaaa"
  p f.flip.("aa", /oo/,"foooo")
  #=> "faaaa"
  p f.flip.curry.("aa").flip.("foooo").(/oo/)
  #=> "(?-mix:oo)" わけのわからん物が返ってきた。

確かに一度カリー化されたProcに対してflipしても効果がない。当然だけど…
でもあきらめちゃだめだ。
上記のアプローチはカリー化されてないものに対してのflipしかない
ならカリー化されてるものに対してのflipを書けばいい。
実装は以下

def flip
  ->f, x, y { f[y][x] }.curry
end

このflipを使って第2引数と第3引数を先に部分適応できる形に変換してやるとこうなる

  f1 = (flip << flip.(f.curry))
  p f1.("aa").("foooo").(/oo/) #元の呼び出しはこう⇒ f.(/oo/,"aa","foooo")
  #=> "faaaa"

変換の途中経過は以下

  # 第1引数と第2引数をひっくり返す
  p flip.(f.curry).("aa").(/oo/).("foooo")
  #=> "faaaa"
  # 元の第1引数と第3引数をひっくり返す
  # つまりひとつ前の第2引数と第3引数をひっくり返す
  p flip.(flip.(f.curry).("aa")).("foooo").(/oo/)
  #=> "faaaa"
  # 最後に関数合成をして元の第2引数を外に追いやる
  # ひとつ前で関数の中に埋ってる"aa"を追いやる
  f1 = (flip << flip.(f.curry))
  p f1.("aa").("foooo").(/oo/) #元の呼び出しはこう⇒ f.(/oo/,"aa","foooo")
  #=> "faaaa"

カリー化されてるProcに対してもflipできた。

Rubyでポイントフリー続きの補足

def g
  ->x, y { x * y }.curry
end

def f
  ->y { y * 4 }
end

def foo(x, y)
   f.(g.(x).(y))
end

あんなわけのわからんことしなくてもこれでいけた。

def foo
#    f.(g.(x).(y))
#    (f << g.(x)).(y)
#    (f.method(:<<) << g).(x).(y)
  (f.method(:<<) << g) # haskelの(f.).gと大体同じになった
end

foo.(2).(2)
#=> 16

関数合成のほうもλにしたりとかわけのわからないことする必要なかった。

Rubyでポイントフリー続き

さっきとは少し変わってgとf関数はλにしてます。

def g
  ->x, y { x * y }.curry
end

def f
  ->y { y * 4 }
end

def foo(x, y)
   f.(g.(x).(y))
end

さて、このfoo(x,y)からxとyを取り除きます。
結果…

def foo
  ((f.<<).<<.(g)) #  f(g(x,y))
end

foo.(2).(2)
#=> 16

もう何をやっているのか分からないよ・・・。


関数合成のほうもλにしないと自分のRuby力じゃできなかったのでこうしました。

module ComposableFunction
# 元コード
#   def >>(g)
#     -> *args { g.to_proc.(self.to_proc.(*args)) }
#   end
#
#   def <<(g)
#     g.to_proc >> self
#   end
  def >>
    ->g { -> *args { g.to_proc.(self.to_proc.(*args)) } }
  end

  def <<
    ->g { g.to_proc.>>.(self) }
  end
end

[Proc, Method, Symbol].each do |klass|
  klass.send(:include, ComposableFunction)
end

Rubyでポイントフリー

まずは普通に関数定義するとこんなんですよね?

def g(x)
end

def f(y)
end

def foo(x)
  f(g(x))
end

foo関数をポイントフリーにしてみます。
まずは、関数合成

def foo(x)
  (method(:f) << method(:g).(x)
  #もしくは ->x { f (g x) }
end

合成したので(method(:f) << method(:g)が1この関数として見れるようになりました。
あとは(x)を消すだけです。

def foo
  (method(:f) << method(:g)
end

fooから引数である(x)が消えた!!?
これだけです。(誰得
まぁ呼び元で

foo.(1) #もしくは foo[1]

みたいになるのでrubyだとちょっと意味分からなくなりそうです。

RubyでMaybeモナド

まずは、データとlookup関数を定義

db = {:alice => {:title => "Ms.", :job => "sales"},
      :bob => {:title => "Mr.", :job => "engineer"}}

def lookup(key)
  ->data {
    data.key?(key) ? Just.return(data[key]) : Nothing
  }
end

lookupのテスト

  p return!(db) >= lookup(:bob)
  #=> <Just:0x882b588 @value={:title=>"Mr.", :job=>"engineer"}>
  p return!(db) >= lookup(:bob) >= lookup(:job)
  #=> <Just:0x882b04c @value="engineer">
  p return!(db) >= lookup(:chris) >= lookup(:job)
  #=> <Nothing:0x8814504 @value=nil>

モナド則を満たしているかやってみる。

  p "(return x) >>= f == f x"
  a = Maybe.return(db) >= lookup(:bob)
  b = lookup(:bob).(db)
  p a == b
  #=> true
  
  p "m >>= return == m"
  a = Maybe.return(db) >= method(:Maybe.return)
  b = Maybe.return(db)
  p a == b
  #=> true
  
  p "(m >>= f) >>= g == m >>= (\\x -> f x >>= g)"
  a = (Maybe.return(db) >= lookup(:bob)) >= lookup(:job)
  b = Maybe.return(db) >= (->x { lookup(:bob).(x) >= lookup(:job) })
  p  a == b  
  #=> true

ちゃんと満たしてる。

実装は以下

まずはhaskellでのモナドの定義

class  Monad m  where
    (>>=)   :: m a -> (a -> m b) -> m b
    (>>)    :: m a -> m b -> m b
    return  :: a -> m a
    fail    :: String -> m a

    m >> k  =  m >>= \_ -> k
    fail s  = error s

上の定義を見ながらrubyならこんな感じだろうと

class Monad
  attr_reader :value
  
  private_class_method :new
  
  def initialize(value = nil)
    @value = value
  end
  
  def >=(f = Proc.new)
    case f
    when Proc
      f[@value]
    else
      f.to_proc[@value]
    end rescue fail("error")
  end
  
  def >(f = Proc.new)
    self >= ->_ {
      case f
      when Proc
        f[]
      else
        f.to_proc[]
      end rescue fail("error")
    }
  end
  
  def self.return(value)
    new value
  end
  
  def fail(s)
    Monad.new s
  end
  
  def ==(other)
    self.class == other.class && @value == other.value
  end
end

モナドの定義ができたので次はMaybeの定義
haskellではこう

instance  Monad Maybe  where
    (Just x) >>= k   =  k x
    Nothing  >>= k   =  Nothing
    return           =  Just
    fail s           =  Nothing

上の定義を見ながらrubyなら…(略

class Maybe < Monad
  def self.return(value)
    Just.return value
  end
    
  def fail(s)
    Nothing
  end
    
  def >=(f = Proc.new)
    case self
    when Nothing; return Nothing
    when Just
      case f
      when Proc
        f[@value]
      else
        f.to_proc[@value]
      end rescue fail(nil)
    end
  end
end

あとはJustとNothingを定義
特異メソッドのself.returnを定義してるのは単に
Just.newと書くと間抜けだなと思いJust.returnと書くようにしてみたかっただけ。

class Just < Maybe
  def self.return(value)
    new value
  end
end

require 'singleton'

class Nothing < Maybe
  include Singleton
  def self.return(value)
    Nothing
  end
end
Nothing = Nothing.instance

ちなみに、>=のバインドメソッドはprocかblockを受け取れるようになっているが
a >= &:to_sとかやるとシンタックスエラーになるので渡せない。なんて残念…
ただし、 Procでないものはto_procしてから呼ぶようにしたので
a >= :to_sは可
どうしても&:to_sを渡したいんだけど!!!って場合は、
a.>=(&:to_s)とかすればシンタックスエラーを回避できる。
>=は引数を必ず一つとるが、引数なしのProcを渡したい場合は>で実現できる。


応用として
MaybeのJustかNothingを返さない関数でも
関数合成をしてやればつなげられる。

def something(x)
  x
end

def mreturn(value)
  Maybe.return value
end

Maybe.return(db) >= method(:Maybe.return) << method(:something) >= method(:mreturn) << method(:something)
#=> <Just:0x8b472bc @value={:alice=>{:title=>"Ms.", :job=>"sales"}, :bob=>
#     {:title=>"Mr.", :job=>"engineer"}}> #ホントは一行

この場合結果はかならずJustなので失敗しないわけだが。

関数合成の実相は以下で昨日も書いた下記からの借り物
http://yuroyoro.hatenablog.com/entry/2012/08/09/203959

module ComposableFunction
  def >>(g)
    -> *args { g.to_proc.(self.to_proc.(*args)) }
  end

  def <<(g)
    g.to_proc >> self
  end
end

[Proc, Method, Symbol].each do |klass|
  klass.send(:include, ComposableFunction)
end