( ꒪⌓꒪) ゆるよろ日記

( ゚∀゚)o彡°オパーイ!オパーイ! ( ;゚皿゚)ノシΣ フィンギィィーーッ!!!

Jetty7のWebSocketをScalaから使う

HTML5っ!WebSocketっ!サーバーからプッシュでっ!


やりますよscalaで。とはいえ、Javaでも同じなので、Javaでやってみようって人にも参考になるかも?

サーバーの実装

サーバ側の実装は、要点をまとめるとこんな感じです。

  • org.eclipse.jetty.websocket.WebSocketServletを継承したServletを作ります。
    • protected abstract WebSocket doWebSocketConnect(HttpServletRequest request, String protocol) を実装しますよ
    • クライアントからws:/hostname/でWebSocketの接続要求が来たら、このdoWebSocketConnectが呼ばれます。
    • doWebSocketConnectでは、org.eclipse.jetty.websocket.WebSocketを実装したオブジェクトを生成して返すようにします。
  • WebSocketの実装クラスでは
    • クライアントとの接続が確立したら、void onConnect(WebSocket.Outbound outbound) が呼び出されます。
    • WebSocket.Outboundオブジェクトってのは、クライアントとのSocketみたいなものだとおもっておけばよいです
    • クライアントにメッセージを送るには、WebSocket.OutboundオブジェクトのsendMessage(byte frame, String data) を呼び出せばよいです。
    • クライアントからメッセージを受信したら、void onMessage(byte frame, String data) が呼び出されます。
    • 接続が切られた場合は、void onDisconnect() が呼び出されます。

簡単ですね。実際はこんな感じのコードです。

package com.yuroyoro.websocket

import scala.collection.mutable.Set
import javax.servlet.http._
import org.eclipse.jetty.websocket._
import org.eclipse.jetty.websocket.WebSocket.Outbound

class ChatServlet extends WebSocketServlet {
  val clients = Set.empty[ChatWebSocket]

  override def doGet(req:HttpServletRequest, res:HttpServletResponse ) =
    getServletContext.getNamedDispatcher("default").forward(req, res)

  override def doWebSocketConnect(req:HttpServletRequest, protocol:String ) =
    new ChatWebSocket

  class ChatWebSocket extends WebSocket {

    var outbound:Outbound = _

    override def onConnect(outbound:Outbound ) = {
      this.outbound = outbound
      clients += this
      onMessage( 0, "WebSocket is success!!!");
    }

    override def onMessage(frame:Byte, data:Array[Byte], offset:Int, length:Int ) = {}

    override def onMessage(frame:Byte, data:String ) =
      clients.foreach{ c => c.outbound.sendMessage( frame, data ) }

    override def onDisconnect = clients -= this
  }
}

クライアント

論よりソースってことで。

    var ws = new WebSocket("ws://localhost:8080/");
    ws.onmessage = function(m) {
      $('#content').prepend(m.data );
    };
    $('#hashtag').change(function(){
      ws.send(this.value);
    });
    $(window).unload(function(){
      ws.close();
    });
  • サーバと接続するにはWebSocketオブジェクトを作ります。引数はURLです。
  • メッセージを送るには、WebSocketオブジェクトのsend( value )を呼ぶだけ
  • サーバからメッセージを受け取るには、WebSocketオブジェクトのonmessageにcallbackをつっこんでおくだけ
  • 接続をきるには、WebSocketオブジェクトのclose

ほんと簡単ですね。


今のところWebSocketをサポートしてるのはGoogle ChromeだけなのでChromeでやってね★

動かしかた

Jettyは、7.0.1.v20091125が必要です。あと、jetty-websocket-7.0.1.v20091125.jarとかがいります。このへんの必要そうなのはJettyのサイトから落っことしてください。


俺は面倒なのでmavenでやりました。pom.xmlの参考にどぞ


あとは、普通のServletアプリケーションのように、WebSocketServletを継承したServlet作ってweb.xmlに入れておけばいいです。


mavenでやる場合の注意点として、どうも今時点ではmvn jetty:runで起動した場合はWebSocketはうまく動作しないようです。(F.Y.I Re: [jetty-users] Re: NPE in WebSocketServlet)


なので、JettyのLauncher作りました。
scala-websocket/hashtag/src/main/scala/com/yuroyoro/websocket/JettyLauncher.scala at master · yuroyoro/scala-websocket · GitHub

サンプル

サンプルを2つほど作りました。おなじみのChatと、Twitterハッシュタグ検索をサーバからプッシュするヤツ。ScalaのActorとWebSocketでこういうリアルタイムプッシュができてうれしいですよね?


GitHubに置いてあります。
yuroyoro/scala-websocket · GitHub

サンプルその1 チャット

Scalaで書いてます。ソースはさっきはったので省略。
scala-websocket/chat at master · yuroyoro/scala-websocket · GitHub

サンプルその2 Twitterハッシュタグ検索

ScalaのActorで更新があったらプッシュします。
scala-websocket/hashtag at master · yuroyoro/scala-websocket · GitHub

画面はこんな感じ。
f:id:yuroyoro:20100316192227p:image

package com.yuroyoro.websocket

import javax.servlet.http._
import org.eclipse.jetty.websocket._
import org.eclipse.jetty.websocket.WebSocket.Outbound
import scala.actors._
import scala.actors.Actor._
import scala.xml.XML
import scala.io.Source

class HashTagServlet extends WebSocketServlet {

  override def doGet(req:HttpServletRequest, res:HttpServletResponse ) =
    getServletContext.getNamedDispatcher("default").forward(req, res)

  override def doWebSocketConnect(req:HttpServletRequest, protocol:String ) =
    new HashTagWebSocket

  class HashTagWebSocket extends WebSocket {

    var outbound:Outbound = _

    object SearchActor extends Actor{
      def act() = {
        react {
          case Search( frame, tag, sinceId) => {
            val url = "http://search.twitter.com/search.atom?q=%%23%s&since_id=%s".format( tag, sinceId )
            val l =  XML.loadString( Source.fromURL( url, "utf-8").getLines.mkString) \\ "entry"
            println( "fetch:%s".format( url ))

            l.map{ e =>
                val c = (e \\ "content" toString)
                  .replace("&lt;", "<").replace("&gt;", ">")
                  .replace("&quot;", "\"").replace("&amp;", "&")
                val Some(img_url) = e \ "link" find { e => e \ "@rel" == "image" }
"""<div class="status">
  <span class="profile_img"><img src="%s"/></span>
  <span class="user_name">%s</span>
  <span class="message">%s</span>
</div>
""".format( img_url \ "@href" text, e \\ "author" \\ "name" text,c )
            }.reverse.foreach{ e => println(e);outbound.sendMessage( frame , e.toString ) }

            Thread.sleep(1000 * 30 )
            val lastId = ( sinceId /: l.map{ e => ( e \ "id" text).split(":").last.toLong }){
              (n, m) => if( n > m ) n else m }
            println( "lastId:%s".format( lastId))
            SearchActor ! Search( frame, tag , lastId)
            act()
          }
          case Dispose =>
        }
      }
    }

    override def onConnect(outbound:Outbound ) =  this.outbound = outbound

    override def onMessage(frame:Byte, data:Array[Byte], offset:Int, length:Int ) = {}

    override def onMessage(frame:Byte, data:String ) = {
      SearchActor.start
      SearchActor ! Search( frame, data, 0 )
    }

    override def onDisconnect = SearchActor ! Dispose

  }
}

case class Search( frame:Byte, tag:String , sinceId:Long)
case class Dispose()

GAE/JでScalaとSlim3使って恵方を知ることができるサービス作ったよ(誰得?)

Google App EngineScalaとSlim3使ってヘンなサービス作りました。


恵方's - 恵方ってどっちよ?


なんと! 今年の恵方だけでなく、去年や来年の恵方までわかってしまう画期的なサービスです!!


f:id:yuroyoro:20100311190335p:image



Google App EngineScalaとSlim3の話は、appengine ja night #6 Beer Talk : ATNDでLTする予定です。

slim3-genのscala版作ったりでちょっと頑張りました。


たぶん、来年の節分の頃には、こんなサービス作ったことすら忘れているでしょう。

NetBeansとScalaを使ってAppEngineたんといちゃいちゃする方法

「新しいアプリだよ。さぁ、デプロイするからAppSlotを解放するんだ…!」
appengineたん「で、でぷろい…ですか…?こんなおっきなあぷり…は、入るかな…?」
「今日はScalaを使ったアプリケーションなんだよ」
appengineたんScalaなんて…そんな変態的なこと…で、できません ///」
「もう遅いよ。どうだ? どんどんアプリがアップロードされていくぞ!」
appengineたん「は、入りました…。こんなおっきなアプリケーション…あついです…」
「よしテストだ。どんどんリクエストをおくってやるからな」
appengineたん「そ、そんなにリクエストされたら…らめぇっ!!SpinUpしちゃうぅっ!!」
「まだまだいくぞ。おらっ!データストアにputしてやるっ!」
appengineたん「らめぇぇ!あっ、あふれちゃうっ!!データが…quotaからあふれちゃうよぉぉっ!!」


自分の頭の悪さに絶望しました。どうみても手遅れです本当にあり(ry


ねたはさておき、Scala2.8 + NetBeans6.8で、Google App Engine/Javaの環境の作り方についてまとめました。

必要なもの

NetBeanでScala

NetBean6.8入れる

まずはNetBean6.8を入れますよ。ダウンロー丼してインスコれ!!
http://netbeans.org/downloads/

NetBeans Scala plugin

次に、NetBeans Scala pluginを入れます。
http://wiki.netbeans.org/Scala68v1#Install_with_NetBeans_6.8


ダウンロードしたファイルを適当なとこに解凍しておきます。


次に、NetBeansのメニューから[ツール]→[プラグイン]を選び、[ダウンロード済み]のタブを開きます。[プラグインの追加]ボタンを押して、先ほど解凍したNetBeans Scala pluginのフォルダを表示させ、*.nbmファイルを全て選択します。


f:id:yuroyoro:20100222153624p:image


これでインストールを押すと、プラグインがインストールされます。

MaxOSXの人は、Scala実行環境のパスを環境変数に設定しておく必要があります。


NetBeansをインストールしたフォルダ(通常は"/Applications/NetBeans/NetBeans 6.8.app/Contents/MacOS")に、environment.plistと言うファイルを以下の内容で作ります。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>SCALA_HOME</key>
  <string>/Users/ozaki/dev/Scala/scala-2.8.0.Beta1-prerelease/rt</string>
  <key>PATH</key>
  <string>/opt/local/bin:/opt/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin:/Users/ozaki/dev/Scala/scala-2.8.0.Beta1-prerelease/rt/bin</string>
</dict>
</plist>

SCALA_HOMEには、Scala2.8をインスコしたパスを、PATHには、Scala2.8のbinディレクトリを絶対パスで追加します。このファイルを作成したら、一度ログアウトしないと有効になりませんので注意が必要です。

Scala Libraryの追加

[ツール]→[ライブラリ]を選び、"新規ライブラリ"を選びます。
名前に"Scala28"と入力し、"jar/フォルダを追加"を押して、Scala 2.8をインストールしたディレクトリにある"lib/scala-library.jar"を選択して追加します。

f:id:yuroyoro:20100222153626p:image
f:id:yuroyoro:20100222153627p:image

Google App Engin Pluginを入れる

Google App Engine SDK

まず、Google App Engine SDKが無ければダウンロー丼しておくべし。

Google App Engine Plugin

[ツール]→[プラグイン]を選び、[設定]のタブを開きます。[追加]ボタンを押して、"http://kenai.com/projects/nbappengine/downloads/download/Latest_NetBeans68/updates.xml"を追加します。

f:id:yuroyoro:20100222153628p:image


[使用可能なプラグイン]タブを開いて[カタログを再読込]すると、GoogleAppEngineのプラグインが一覧に表示されますので、全て選択して[インストール]します。

f:id:yuroyoro:20100222153629p:image




プラグインのインストール後に、GoogleAppEngineのサーバーをサービスに追加します。
左側の[サービス]タブを表示し、"サーバー"を右クリックして[サーバーの追加]を選びます。

f:id:yuroyoro:20100222153630p:image


"Google App Engine"を選択して次へ。

f:id:yuroyoro:20100222153631p:image


"Installation Location"には、Google App Engine SDKを解凍したディレクトリを指定します。

f:id:yuroyoro:20100222154249p:image

Google App Engine Libraryの追加

[ツール]→[ライブラリ]を選び、"新規ライブラリ"を選びます。


名前に"AppEngineLibs"と入力し、"jar/フォルダを追加"を押して、Google App Engine SDKを解凍したディレクトリにある"user/appengine-api-1.0-sdk.jar"を選択して追加します。

f:id:yuroyoro:20100222154250p:image
f:id:yuroyoro:20100222154251p:image

サンプルプロジェクトを動かしてみる

Google App Engine SDKに付属しているサンプルアプリを動かしてみましょう。
[プロジェクト]→[新規作成]を選び、"サンプル"の中にある"Google App Engine"のGuest Bookを選びましょう。

f:id:yuroyoro:20100222154252p:image


できたプロジェクトを実行すると、ブラウザで動作が確認できます。

ScalaでApp Engineを動かしてみる

Webアプリケーションプロジェクト

では、ScalaでApp Engineを動かしてみましゅ。

[ファイル]→[新規プロジェクト]を選んで、"Java Web"の"Webアプリケーション"を選択します。

f:id:yuroyoro:20100222154253p:image


プロジェクト名などを入力して、"サーバーと設定"のところでサーバに"Google App Engine"を選んで完了です。
f:id:yuroyoro:20100222154254p:image

Scalaプロジェクト

Scalaを使う場合、Webアプリケーションプロジェクトとは別にScalaプロジェクトを用意します。このプロジェクトを先ほど作ったWebアプリケーションプロジェクトから参照させる形です。

[ファイル]→[新規プロジェクト]を選んで、"Scala"の"Scala Class Library"を選択します。
f:id:yuroyoro:20100222154255p:image


出来たプロジェクトのライブラリを右クリックして[ライブラリを追加]を選び、"AppEngineLibs"と"Scala28"を追加します。

f:id:yuroyoro:20100222154256p:image

プロジェクトを→クリックして、[新規]→[その他]から"Scala"の"Scala Class"を選んで新規作成します。
f:id:yuroyoro:20100222154257p:image

内容をこんな感じにします。

package testapp
class FirstScala {
  def someMethod() = {
    "This comes from Scala!!"
  }
}
Webアプリケーションプロジェクトを変更する

Webアプリケーションプロジェクトのライブラリを右クリックして[ライブラリを追加]を選び、"AppEngineLibs"と"Scala28"を追加します。

f:id:yuroyoro:20100222154256p:image

次に、Webアプリケーションプロジェクトのライブラリを右クリックして、[プロジェクトを追加]を選びScalaプロジェクトを追加します。

f:id:yuroyoro:20100222154258p:image

"Webページ"にあるindex.jspを修正します。

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
   "http://www.w3.org/TR/html4/loose.dtd">
<%@page import="testapp.FirstScala"%>
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>JSP Page</title>
    </head>
    <body>
        <h1>Hello World!</h1>
         <p>Test: <%= new FirstScala().someMethod() %></p>
    </body>
</html>
実行

Webアプリケーションプロジェクトを実行すると、ブラウザに画面が表示されます。

f:id:yuroyoro:20100222154259p:image

もしここで、SCALA_HOMEが読み込めないというAntのエラーが出る場合は、 Scalaプロジェクトの"nbproject/build-impl.xml"を開き、-init-privateターゲットないでscala.homeを絶対パスで設定するようにしてみてください。

    <target depends="-pre-init" name="-init-private">
        <property file="nbproject/private/config.properties"/>
        <property file="nbproject/private/configs/${config}.properties"/>
        <property file="nbproject/private/private.properties"/>
        <property environment="env"/>
        <condition property="scala.home" value="${env.SCALA_HOME}">
            <isset property="env.SCALA_HOME"/>
        </condition>
        <property name="scala.home" value="/Users/ozaki/dev/Scala/scala-2.8.0.Beta1-prerelease/rt"/>
        <property name="scala.lib" value="${scala.home}/lib"/>
        <fail unless="scala.home">
                    You must set SCALA_HOME or environment property and append "-J-Dscala.home=scalahomepath"
                    property to the end of "netbeans_default_options" in NetBeansInstallationPath/etc/netbeans.conf to point to
                    Scala installation directory.
        </fail>

        
        <property name="scala.compiler" value="${scala.lib}/scala-compiler.jar"/>
        <property name="scala.library" value="${scala.lib}/scala-library.jar"/>
        <taskdef resource="scala/tools/ant/antlib.xml">
            <classpath>
                <pathelement location="${scala.compiler}"/>
                <pathelement location="${scala.library}"/>
            </classpath>
        </taskdef>

    </target>

Scala hack-a-thon #2 開催しました。

Scala hack-a-thon #2、無事終了しました。
参加された皆さん、本当に有り難うございました。


やっぱり、誰もしゃべらず黙々とコード書いたり資料読んだりで、まじで「絶対にしゃべってはいけない Scala hack-a-thon」でした。
しかし、みなさんそれなり楽しんでいただけたようで何よりです。


当日#scalahackでのついったーの発言をまとめました。
Scala hack-a-thonまとめ - Togetter


会場は、Oracleさんにご提供頂きました。無線LAN電源完備で、無限コーヒーと青山の夜景つきというすばらしい会場で、本当に感謝しております。


今回は、発表の時間を2時間ほどとりました。当初予定していた方以外に、飛び込みのLTの応募をたくさん頂いて、ほんとにうれしかったです。もっと時間枠を取ればよかったかな?


当日発表して頂いた方たちです。本当に有り難うございました。

3回目は、グループ分けしてハンズオンしたり、発表枠をもう少し取ったりしようかなと思います。
3回目の日程はまだ未定ですが、そのうちやりますのでぜひご参加ください。


今回使用した資料は、前回からあまり更新されてません><
Genricsについて少し書いたのと、Githubの方にサンプルとしてEchoサーバとFeedからマルコフ連鎖するやつを追加しています。


yuroyoro/scala-hackathon · GitHub
Dropbox - 404


ってことで、繰り返しになりますが、皆さん本当に有り難うございました。

Scala hack-a-thon #2に参加される皆様へ

えと、もう明日なんですが、2月20日(土)に、Scala hack-a-thon #2が開催される予定となっております。


Scala Hack-a-thon #2 : ATND


つきましてはですね、主催である私目から皆様へ、どのような趣旨のイベントなのかと、いくつかのお願いをこの場を借りてさせて頂きたい所存でございますことよ。


(相変わらず日本語がトチ狂っているのは無視してください><)

コンセプト

このイベントのコンセプトは、「みんなで集まってコードを書く」これにつきます。


各自がそれぞれのペースでコードを書いて、わからないところは誰かに聞く。参加者の自主性を重んじるイベントと、あいなっております。


とはいえ、当日初めてScalaに触れる方もいらっしゃるでしょうし、それなりの資料は用意するつもりです。


死霊はこちらです。都度更新する予定です。


yuroyoro/scala-hackathon · GitHub
Dropbox - 404

当日の内容

基本は、各自でコード書いてもらうんですが、初めてScalaに触れる方のためにハンズオンをします。大体2時間くらいです。


それがおわったら、用意した死霊にそってやってもらうなど各自の自主性に任せます。


あと、 17:00くらいから発表もやる予定です。


今のところ、以下の方に発表して頂く予定ですが、当日の飛び入り参加も全然おけですよ!
プレゼン資料なんて飾りです。エライ人にはそれがわからんのですよ!!


- Liftについて(俺)
- play frameworkとsienaで簡単GAEアプリ作成 (@k_nishijimaさん)
- PEGパターンマッチングライブラリpegexのデモ(@kmizuさん)

お願い

Scalaの実行環境とAPIドキュメントは、当日USBメモリで配布するつもりですが、できれば事前に準備しておいてもらえるとありがたいです。


環境の作り方については、こちらで資料を用意しています。
http://github.com/yuroyoro/scala-hackathon/raw/master/doc/source/setup.pdf


会場は、Oracleさんのご厚意でご提供頂いております。電源、無線LAN完備のすばらしいところです。キレイに使いましょう。
ゴミは持ち帰りましょう。


感想をBlogで書いてもらえると、感激のあまり体中の穴という穴からなんらかの汁があふれ出してしまうかもしれないです。


最後に、とっても大事なことですが。


ついったーだけじゃなくリアルでも会話しましょう

懇親会

19:00から、こちらです。牛すき焼きコース3,600円飲み放題です。


ぐるなび - 北の家族 青山店

最後に

開催に当たって、できる限りの準備をするつもりですが、いろいろ至らない点も多々あるかと思います。


皆様のご協力をお願いいたします。


この機会に、Scalaを使ってくれる人が増えること、そして、当日集まって頂いた皆さんと、今後オン/オフ問わず、交流できていけばいいなぁと思っています。


では、当日は宜しくお願いいたします。

草生やす関数

草生やしたいときに

def www( s:String ):String = {
  import scala.util.Random
  val rnd = new Random( )  
  val ws = List( "w","W","w" )
  def w(n:Int):String = ( 0 - n) until rnd.nextInt(3) map { i => ws( rnd.nextInt( ws.size -1 ) ) } mkString
  
  ("ちょ" + w(1) + "おま" + w(1) ) + ("" /: s ){ ( c,e ) => c + e + w(0) } + w(0)
}

実行結果:

scala>   www("誰得よこんなん")
res1: String = ちょWWおまw誰得よWWこんwなんWwW

ちょっとした問題。Listから要素を検索して残りの要素数を返す。どう書く?

ちょっと面白い問題を見つけたので、俺も解いてみました。


Blog not found


仕様はこうです。

  1. あるListから要素を探し、該当する要素を除いた残りの要素数を返す。
  2. ただし、要素がListに存在しない場合は-1を返す。


1の仕様だけなら、単純にList#dropWhileしてlengthを返すだけでしょうが、2を考えるとそうも行きません。
存在しない場合もListの末尾に該当する場合もともに結果が0になってしまうからです。


実行結果はこんな感じになります。

scala> val list = List("World", "is", "not", "enough")
list: List[java.lang.String] = List(World, is, not, enough)

scala> remainsLength( list , "is")
res1: Int = 2

scala> remainsLength( list , "foo")
res2: Int = -1

scala> remainsLength( list , "enough")
res3: Int = 0


俺はScalaで解きましたが、他の言語でやってみるのも面白いかとおもいます。

ストレートにやってみる

List#dropWhileだけではダメなら、最初に要素が存在するかList#findで検索して、結果のOption型をmatchで分けてやればよいと考えたのが以下のものです。

def remainsLength[T]( l:List[T],v:T ) = l.find{ v == } match { 
  case Some(_) => l.dropWhile{  v!= }.size - 1
  case None => -1 
}


よく考えたら、List#indexOfでいんじゃまいかと思って書いたのが以下のものです。考え方自体はfindと変わってないです。

def remainsLength[T]( l:List[T],v:T ) = l.indexOf( v ) match {
  case -1 => -1
  case n  => l.drop( n + 1 ).length
}


ただ、どっちも matchを使ってるんでいまいちな感じ。

再帰でやってみる

再帰でやるなら、Listのパターンマッチで、先頭要素が該当するまで再帰で頭からListを喰っていけばよいはずです。

def remainsLength[T]( l:List[T],v:T ):Int = l match {
  case Nil => -1 
  case `v` ::xs => xs.length
  case x::xs => search( xs , v)
}


caseに書くセレクタで`(バッククォート)で囲むと、パターンマッチ変数にならずに評価されるというのは初めて知りました。
このパターンマッチは以外と有用かも?

追記

@ymnkさんにもっとエレガントな回答を頂きました。

def search[T](l: Seq[T], s:T)=l.dropWhile(s!=_).length-1 


dropWhileで返す要素が、検索対象を含む場合は最小で1、含まない場合は0だから、単純にlength-1をするだけでおけと。

追記その2 無理矢理なimplicit conversionでOptionを拡張してみた

Optionに、Someだったら中身を引数の関数に渡して、Noneだったらdefaultを返すような関数が無かったので、無理矢理implicit conversionで対応してみたのがこれ。

class MapOrElseOption[A]( o:Option[A] ){

  def mapOrElse[B]( default : => B)( f:(A) => B ) = o match {
    case None => default
    case Some(x) => f(x)
  }
}

implicit def option2MapOrElseOption[A]( o:Option[A] ) = new MapOrElseOption( o )


def remainsLength[T]( l:List[T],v:T ) = l.find{ v == }.mapOrElse( -1 ){ _ => l.dropWhile{  v!= }.size - 1 }