2012-02-14 11 views
3

私は、次のような状況を持っている:Scalaの一般的な機能の混乱

は、その変換のリストを返す結果(すべてのそれらのAny年代はケースクラスです)のいくつかの変換を行う方法def f(lst: List[Any])を、手に入れました。私が達成する必要があるのは、入力リストが空で、リストを生成し、正しいタイプの要素を1つ含み、それを使ってその変換を行う場合です。

型レベルで、あるケースクラスに引数なしのコンストラクタがあることを保証できますか?もしそうなら、何をAnyと置き換えるべきですか?いいえ、これを達成する最良の方法は何ですか?たぶん、私はちょうどdef f[T](lst: List[T], default: T)のようなものに自分のメソッドを変更する必要がありますか?

助けてください。

+3

注: 'F'メソッド、関数ではなく。関数は '=>'(基本的には 'FunctionN'特性の1つを匿名で拡張し、' apply'をオーバーライドするための文法的砂糖です)で作成され、 'def'メソッドを定義します。 –

+0

ご同意いただき、ありがとうございます。 – folone

答えて

5

あなたはこのようなものをお探しですか?

import scalaz._ 
import Scalaz._ 

scala> def f[A : Zero](lst: List[A]) = { 
    | val xs = if(lst.isEmpty) List(mzero[A]) else lst 
    | xs ++ xs // some transformation 
    | } 
f: [A](lst: List[A])(implicit evidence$1: scalaz.Zero[A])List[A] 

scala> f(List.empty[Int]) 
res1: List[Int] = List(0, 0) 

scala> f(List("hello", "world")) 
res2: List[java.lang.String] = List(hello, world, hello, world) 

そうならば、あなたは私がしばらく前に被写体に書いたthis postを参照することができます。

+0

はい、ありがとうございます。 – folone

1

あなたは何をしようとしているのかはっきりしていませんが(多分詳細をいくつか含めることができます)、すぐに言えるアドバイスは、関連するケースクラスがたくさんある場合、封印された形質。これにより、型の安全性が向上するだけでなく(Anyもありません)、コンパイラは網羅的なパターンマッチングをチェックできます。

sealed trait Foo 
case class Bar(x: Int) extends Foo 
case class Baz(y: String) extends Foo 

次に、あなたがそう

def f[T <: Foo](lst: List[Foo], default: T)//... 

これはlistはケースクラスのいずれかの項目を含めることができますが、defaultはタイプによって指定されたタイプであることが必要になりますように、あなたの関数を定義することができます。たとえば、パラメータ(サブタイプはFooである必要があります)

2

単純な答えはいいえです。型システムは、クラスにデフォルトのコンストラクタがあるかどうかを伝えることはできません。引数のない大文字小文字のクラスは非推奨であるため、通常、大文字と小文字のクラスにはデフォルトのコンストラクタがありません。デフォルトコンストラクタのコンセプトは、不変オブジェクトではそれほど有用ではありません。 AFAIKでは、原理的には存在しないべき理由はありません(スカラは、型が特定の名前のメソッドを持たなければならない構造型をサポートしています)が、言語の変更が必要です。あなたはリフレクションでランタイムにチェックすることができますが、それはあなたが望むものではありません。

ただし、typeクラスパターンを使用すると、デフォルト値を有効範囲にすることができます。これは概念的には、OPで示唆されているような追加のデフォルトの引数を追加することに非常によく似ていますが、implicitsを使用してそれらを隠すことです。これはコレクションライブラリで頻繁に使用されています。この場合、scalaz.Zeroを使ったmissingfaktorの答えは特別なケースですが、バニラのScalaや、ある種のゼロではない任意のデフォルトでは、やりにくいです。

case class Default[T](default: T) 

case class Foo(value: String) 
case class Bar(value: Int) 

implicit val fooDefault = Default(Foo("I'm a default Foo")) // note 1 

今の使用例を見てみましょう:

def firstItem[T](lst: List[T]) (implicit ev: Default[T]) = // note 2 
    if (lst.isEmpty) ev.default else lst.head 

val fooList  = List(Foo("cogito"), Foo("ergo"), Foo("sum")) 
val emptyFooList = List[Foo]() 
val barList  = List(Bar(101), Bar(102)) 
val emptyBarList = List[Bar]() 

firstItem(fooList)      // Foo("cogito") 
firstItem(emptyFooList)     // Foo("I'm a default Foo") 
firstItem(barList)      // ** error: missing implicit ** 

だから私たちは、これがList[Foo]でコンパイルしますが、暗黙Default[Bar](注3)が存在しないので、List[Bar]が受け入れられないことがわかります。


注1:この暗黙のはobject Fooに定義することができ - あなたが他の場所でクラスをインポートする場合、それがスコープ内にあることを確認しますと思われます。しかし、必ずしもそうである必要はありません。任意のクラスの類似implicitsを定義することもできます(IntString、何でも試してみてください)。

注2:これは、def firstItem[T: Default](lst: List[T]) = ...と同じです。ここでは、evimplicitly[Default[T]]と召喚します。好きなのを選びな。

注3:私たちは、それは単に1を供給することによって動作させることができます。

firstItem(barList)(Default(Bar(42)))  // Bar(101) 
firstItem(emptyBarList)(Default(Bar(42))) // Bar(42) 
関連する問題