2017-10-17 10 views
2

私は、(いくつか、例えば5から10の)オプションの存在に基づいてオブジェクトに変更を適用したい場合があります。私は命令的にそれを行うにしたのであれば基本的には、どのような私が目指してることは次のとおりです。左から右への引数型の推論

var myObject = ... 
if (option.isDefined) { 
    myObject = myObject.modify(option.get) 
} 
if (option2.isDefined) { 
    myObject = myObject.someOtherModification(option2.get) 
} 

(ご注意:多分私のオブジェクトは、多分、それはここでのポイントではないではない、可変である。)

私は(...擬似コード)私のような、これを書いている流暢な方法を実装しようとした場合、それは立派に見えると思った:

myObject.optionally(option, _.modify(_)) 
     .optionally(option2, _.someOtherModification(_)) 

だから私はIntelliJのがハイライト表示されませんサンプルコード、使用を開始エラーとして表示されますが、実際には作成されません。今

class MyObject(content: String) { 
    /** Apply a transformation if the optional is present */ 
    def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = 
     optional.map(operation(_, this)).getOrElse(this) 
    /** Some possible transformation */ 
    def resized(length : Int): MyObject = new MyObject(content.substring(0, length)) 
} 
object Test { 
    val my = new MyObject("test") 
    val option = Option(2) 

    my.optionally(option, (size, value) => value.resized(size)) 
} 

、私の場合には、それは本当にように見えるんどのようなので、MyObjectタイプは、いくつかの外部のAPIであるので、私は助けるために暗黙的な変換を作成しました:

// Out of my control 
class MyObject(content: String) { 
    def resized(length : Int): MyObject = new MyObject(content.substring(0, length)) 
} 

// What I did : create a rich type over MyObject 
class MyRichObject(myObject: MyObject) { 
    def optionally[A](optional: Option[A], operation: (A, MyObject) => MyObject): MyObject = optional.map(operation(_, myObject)).getOrElse(myObject) 
} 
// And an implicit conversion 
object MyRichObject { 
    implicit def apply(myObject: MyObject): MyRichObject = new MyRichObject(myObject) 
} 

そして、

object Test { 
    val my = new MyObject("test") 
    val option = Option(2) 
    import MyRichObject._ 
    my.optionally(option, (size, value) => value.resized(size)) 
} 

今回はIntelliJで失敗し、コンパイル中に0の型は不明です: Error:(8, 26) missing parameter type my.optionally(option, (size, value) => value.resized(size))

それを動作させるために、私ができる:

  1. 積極sizeの引数の型を指定します。my.optionally(option, (size: Int, value) => value.resized(size))
  2. カレーバージョン
optionallyを書き換え
  • 誰も本当に悪いですが、私が聞いてもいいかもしれません:

    • カリーバージョンが動作することを理由があるが、多引数のバージョンがパラメータ化型を推論するために失敗するようで、
    • は、それが実際の種類
    • となどを指定せずに動作するように書くこともできますボーナス(これは意見に基づいているかもしれませんが)、どのように書くのでしょうか?(foldLeftの一連のオプションが私の心に浮かんでいます...)?、移動することですあなたが唯一のオブジェクトの作成から1種類を管理する場合は、

      object Test { 
          val my = new MyObject("test") 
          val option = Some(2) 
          my.optionally[Int](option, (size, value) => value.resized(size)) 
      } 
      

      別の方法:

  • 答えて

    2

    一つのオプション:あなたが言ったように

    // Out of my control 
    class MyObject(content: String) { 
        def resized(length : Int): MyObject = new MyObject(content.substring(0, length)) 
    } 
    
    object MyObjectImplicits { 
    
        implicit class OptionalUpdate[A](val optional: Option[A]) extends AnyVal { 
        def update(operation: (A, MyObject) => MyObject): MyObject => MyObject = 
         (obj: MyObject) => optional.map(a => operation(a, obj)).getOrElse(obj) 
        } 
    
    } 
    object Test { 
        val my = new MyObject("test") 
        val option = Option(2) 
        import MyObjectImplicits._ 
        Seq(
        option.update((size, value) => value.resized(size)), 
        // more options... 
    ).foldLeft(my)(_) 
    } 
    

    は同様にちょうど、あなたのoptionallyのカレー・バージョンを使用することがあります。

    +0

    私は、MyObject(一般化:パラメータ化された型を変換可能にすることによって、反対側のパラメータ化されたメソッドを排除する)ではなく、Optional型にimplicitsを逆転させる考えが好きです。私はあなたの結論に同意する:カレー風のバージョンは、少なくとも良いものに見える。目撃者の種類(https://stackoverflow.com/questions/8524878/implicit-conversion-vs-type-class?rq=1)で試してみたいことがあると思いました – GPI

    +0

    http://pchiusano.blogspotも参照してください。 hk/2011/05/making-most-of-scalas-extremely-limited.htmlあなたが観察した型推論の制限について – PH88

    +0

    それを掘り下げてくれてありがとう。私には分かりませんが、最初の実装例では(暗黙のうちに)、実際にはうまくいきます... – GPI

    1

    タイプを追加する必要性について考えるためのよりよい方法は、それをこのように記述しています、クラス作成への一般的なが、このオプションを使ってあなただけのインスタンスごとに1つのタイプすることができ、注意してください:あなたはジェネリックが徳でいたすべての場所今、見ることができるように

    class MyObject[A](content: String) { 
        def optionally(optional: Option[A], operation: (A, MyObject[A]) => MyObject[A]): MyObject[A] = 
        optional.map(operation(_, this)).getOrElse(this) 
        def resized(length : Int): MyObject[A] = new MyObject[A](content.substring(0, length)) 
    } 
    
    object Test { 
        val my = new MyObject[Int]("test") 
        val option = Some(2) 
        my.optionally(option, (size, value) => value.resized(size)) 
    } 
    

    をエンint型によって、それはあなたが最初の場所で何を望むかであるので、ここではなぜ言ってかなりの答えです:

    (と思うちょうど一部はここに適用されます:)

    4)とき推測される戻り値の型は、あなたが意図したよりも一般的です(例:Any)。

    出典:あなたの検討のためのIn Scala, why does a type annotation must follow for the function parameters ? Why does the compiler not infer the function parameter types?

    +0

    最初の提案をお寄せいただきありがとうございます。振り返ってみると明らかに見えますが、これは一種のものです。あなたの2番目の命題も素晴らしいですが、制限がありますし、実際にMyObjectを変更することはできません。あなたの答えの残りの部分は、暗黙のうちに推論が働く理由が説明されないので、適用されないようです... – GPI

    +0

    @GPIの場合はルール4)が適用されます:4)推論された戻り値の型がより一般的であるあなたが意図したよりも、例えば、Any。 一般的なタイプを返す場所を指定します。特定のタイプが必要な場合は、同意しませんか? –

    +0

    @GPIとにかく私は答えを明確にするために重要ではない部分を削除しました。私はいつも助けてくれることを愛しています。 –

    関連する問題