2017-12-04 12 views
4

私は、3つのフィールドからなるデータを持っています。StringInt、およびDoubleとしましょう。Scalaの3ウェイ以上の型のエンコーディング

これらはすべてオプションですが、すべてに少なくとも1つのフィールドが含まれている必要があります。

のようにcats.data.Iorを試しました。それは動作しますが、実際にそれを行うときに少し不器用だと感じます。 case classを3つのOptionで作成し、すべてがNoneの場合はコンストラクタで例外をスローする必要がありますが、私はその考えが嫌です。

これを実行する別の方法があるかどうか知りたいのですが(好ましくは、Iorのような型の情報のみをすべてエンコードしますが、複雑さが少ないため型の値が複雑ではありません) 。他の図書館を利用することも歓迎します。

+2

3つのオプションの値を持つクラスを使用することは大丈夫だと思います。条件を検証するインスタンスを作成するためのメソッドで、 'Option [YourClass]'、 '[Error、YourClass]'またはコードベースで使用しているものを返すことができます。 – AlexITC

+0

あなたは正しいと思います。私は、それが型だけを使ってこれをエンコードするより自然な方法があるかどうかを知りたいのはまだ興味があります。 – yhm

+0

私は 'cats.Ior'を使うか、あなたの7クラス' trait'をローリングするかは、理論的には健全なアプローチだと思います。 'cats.Ior'のデストラクタは3ウェイ'フォールド 'で、3ウェイタイプの7ウェイ 'フォールド'になります。これは扱いにくいです。あなたはこれらの価値観で何をするつもりですか? 3つのフィールドの間に何らかの自然優先がありますか?この質問に答えることで、「適切な」エンコーディングを簡単に決定することができます。 – ziggystar

答えて

1

あなたはおそらく同様にこのアイデアを考えたが、何このようなものについて:

class StringOrIntOrDouble private(val first: Option[String], val second: Option[Int], val third: Option[Double]) { 
    def this(first: String) = this(Some(first), None, None) 

    def this(second: Int) = this(None, Some(second), None) 

    def this(third: Double) = this(None, None, Some(third)) 

    def this(first: String, second: Int) = this(Some(first), Some(second), None) 

    def this(second: Int, third: Double) = this(None, Some(second), Some(third)) 

    def this(first: String, third: Double) = this(Some(first), None, Some(third)) 

    def this(first: String, second: Int, third: Double) = this(Some(first), Some(second), Some(third)) 

} 

主なコンストラクタは、そうあなたが空のインスタンスを作成することはできませんprivateです。明らかに、ProductequalshashCodetoStringのような実装と、ケースクラスのような他の有用な事柄を拡張することができます(ただし、不変式を簡単に破る可能性があるのでcopyに注意してください)。

残念ながら、汎用バージョン(または2つの場所で同じタイプ)を使用する場合は、名前付きメソッドのすべての「サブセット」コンストラクタをコンパニオンオブジェクトに移動する必要があります。そうしないと、タイプ消去のためにコンパイルされません。

class TripleIor[+A, +B, +C] private(val first: Option[A], val second: Option[B], val third: Option[C]) { 

} 

object TripleIor { 
    def first[A](first: A) = new TripleIor[A, Nothing, Nothing](Some(first), None, None) 

    def second[B](second: B) = new TripleIor[Nothing, B, Nothing](None, Some(second), None) 

    def third[C](third: C) = new TripleIor[Nothing, Nothing, C](None, None, Some(third)) 

    def firstAndSecond[A, B](first: A, second: B) = new TripleIor[A, B, Nothing](Some(first), Some(second), None) 

    def secondAndThird[B, C](second: B, third: C) = new TripleIor[Nothing, B, C](None, Some(second), Some(third)) 

    def firstAndThird[A, C](first: A, third: C) = new TripleIor[A, Nothing, C](Some(first), None, Some(third)) 

    def apply[A, B, C](first: A, second: B, third: C) = new TripleIor[A, B, C](Some(first), Some(second), Some(third)) 
} 

より根本的な方法は、sealed traitと7サブクラスと同じアイデアを実装することですが、私は彼らが使用する方がはるかに簡単だろうとは思いません。また、copyをタイプセーフな方法で実装することができますが、多くのタイプの入力コストがかかります(ここには示されていません)。

sealed trait TripleIor[A, B, C] extends Product { 
    def firstOption: Option[A] 

    def secondOption: Option[B] 

    def thirdOption: Option[C] 
} 

object TripleIor { 

    final case class First[A](first: A) extends TripleIor[A, Nothing, Nothing] { 
    override def firstOption: Option[A] = Some(first) 

    override def secondOption: Option[Nothing] = None 

    override def thirdOption: Option[Nothing] = None 
    } 

    final case class Second[B](second: B) extends TripleIor[Nothing, B, Nothing] { 
    override def firstOption: Option[Nothing] = None 

    override def secondOption: Option[B] = Some(second) 

    override def thirdOption: Option[Nothing] = None 
    } 

    final case class Third[C](third: C) extends TripleIor[Nothing, Nothing, C] { 
    override def firstOption: Option[Nothing] = None 

    override def secondOption: Option[Nothing] = None 

    override def thirdOption: Option[C] = Some(third) 
    } 

    final case class FirstSecond[A, B](first: A, second: B) extends TripleIor[A, B, Nothing] { 
    override def firstOption: Option[A] = Some(first) 

    override def secondOption: Option[B] = Some(second) 

    override def thirdOption: Option[Nothing] = None 
    } 

    final case class SecondThird[B, C](second: B, third: C) extends TripleIor[Nothing, B, C] { 
    override def firstOption: Option[Nothing] = None 

    override def secondOption: Option[B] = Some(second) 

    override def thirdOption: Option[C] = Some(third) 
    } 

    final case class FirstThird[A, C](first: A, third: C) extends TripleIor[A, Nothing, C] { 
    override def firstOption: Option[A] = Some(first) 

    override def secondOption: Option[Nothing] = None 

    override def thirdOption: Option[C] = Some(third) 
    } 

    final case class All[A, B, C](first: A, second: B, third: C) extends TripleIor[A, B, C] { 
    override def firstOption: Option[A] = Some(first) 

    override def secondOption: Option[B] = Some(second) 

    override def thirdOption: Option[C] = Some(third) 
    } 
} 

P.S.ここにあるすべての例は、多くの有用で必要なものを実装していないアイデアを説明するためのスケッチです。

+0

@yhm、あなたは正しいです。特定のクラスに特定のクラスが必要な場合、そのアプローチが有効です。それが一般的でなければならない場合は、タイプ消去のために、「サブセット」コンストラクタの名前を別にして、コンパニオンオブジェクトに移動する必要があります。これにより、コードが醜くなり、密封された形質のアプローチがより魅力的になります。 – SergGr

+0

この場合、私は今、私は特定のクラスだけが必要なので、このアプローチは素晴らしいようです。 – yhm

+1

@yhm、OKだから型固有の事例を取り戻し、実際に本格的なジェネリックソリューションを実装するのが簡単かもしれない封印された特性のスケッチを追加しました。 – SergGr

関連する問題