2016-09-23 3 views
1

Учитывая выдуманный F типа класс:Использования `неявного def`

scala> trait F[A] {} 
defined trait F 

и это определение, которое использует context bound требовать, чтобы входная A имеет типа класс экземпляр F:

scala> def f[A : F](x: A) = ??? 
f: [A](x: A)(implicit evidence$1: F[A])Nothing 

я определил экземпляр в Person и тип класса:

scala> case class Person(name: String) 
defined class Person 

scala> implicit val person: F[Person] = new F[Person] {} 
person: F[Person] = [email protected] 

И следующие компилирует:

scala> f(Person("foo")) 
scala.NotImplementedError: an implementation is missing 

Но нет никакого String реализации, поэтому она выходит из строя.

scala> f("foobar") 
<console>:17: error: could not find implicit value for evidence parameter of type F[String] 
     f("foobar") 
     ^

Затем я определил F[String] с помощью:

scala> implicit def fInstance(x: String) = new F[String] {} 
fInstance: (x: String)F[String] 

Но, я не могу запустить:

scala> f("foobar") 
<console>:18: error: could not find implicit value for evidence parameter of type F[String] 
     f("foobar") 
     ^

, так как я не неявное F[String], а скорее String => F[String].

Что такое правильный способ использовать такую ​​implicit def для удовлетворения F[String] ограничения, то есть вызов функции f успешно с типом String?

Я получил его на работу через:

scala> implicit val x: F[String] = implicitly[String => F[String]].apply("foobar") 
x: F[String] = [email protected] 

scala> f("foobar") 
scala.NotImplementedError: an implementation is missing 
    at scala.Predef$.$qmark$qmark$qmark(Predef.scala:230) 
    at .f(<console>:12) 
    ... 33 elided 

Но я не уверен, если это правильный/чистый способ сделать это.

+0

Это действительно сложно ответить, не зная, какую информацию и операции должен использовать класс типа, но с учетом этого конкретного 'F' вы можете определить экземпляр' String' точно так же, как вы делали 'Person' пример. –

+0

Для этого случая я могу только построить 'F [String]', если при условии 'String', то есть экземпляр' F [String] 'зависит от аргумента' String'. –

+0

В качестве продолжения, плохо ли использовать «неявный» вне класса-компаньона, т. Е. Сиротский «неявный» экземпляр? –

ответ

0

Ваш fInstance определяет неявное преобразование, то есть способ превратить String в F[String]. Для создания экземпляра класса типов, способ приема неявных параметров может быть использован:

implicit def fInstance(implicit x: String) = new F[String] {} 

он обычно используется в библиотеках FP, чтобы получить один класс типов от другого:

implicit def optionMonoid[A](implicit S: Semigroup[A]): Monoid[Option[A]] = ??? 
// or, which is the same 
// implicit def optionMonoid[A: Semigroup]: Monoid[Option[A]] = ??? 

Идея заключается в том, что F[String] может работать на любой String вообще, не зависящей от фактических аргументов, предоставляемых в функцию. Конечно, вы всегда можете предоставить экземпляры в явном виде:

f("foobar")(new F[String] { }) 

В последующем, важной части классов типов является то, что вы можете определить их одноранговыми, т.е. не имеющий доступ к определениям F и String на все , и вы вынуждены охватить неявки в Scala и импортировать их, так что все в порядке.

+0

', и вы вынуждены расширять область видимости в Scala и импортировать их, поэтому все в порядке. 'Не могли бы вы сказать больше о том, как это утверждение применяется (или не) к сиротским «имплицитным»? –

+0

@KevinMeredith вам не разрешено просто писать 'def' или' val' в источнике (если это код приложения, а не скрипт Scala) - он должен принадлежать объекту или классу. Таким образом, вы должны пойти с 'object Implicits {implicit def fString: F [String] = ??? } ', но вы не можете использовать его в другом коде, если только вы не импортируете Implicits.fString в него перед использованием. –

1

Вы определили неявное преобразование. Если вы хотите использовать def для предоставления экземпляров typeclass, вы просто пишете то же самое, что и пишете неявный val, но замените val на def.

implicit def fInstance = new F[String] {} 

Обычно вы используете только def, если вам нужны параметры типа, например здесь.

implicit def fInstance[A] = new F[List[A]] {} 

Или

implicit def fInstance[A](implicit ev: F[A]) = new F[List[A]] {} 
0

Вот упрощенная версия вашего определения (и вы можете удалить implicit из fInstance):

implicit val singleFInstance: F[String] = fInstance("") // or fInstance("foobar"), etc. 

ли это правильная вещь, чтобы сделать, очень многое зависит от того, что должно означать F и f.

Но вообще говоря: если F действительно тип класса, fInstance(string) дает разные результаты в зависимости от строки (а не только различных экземпляров, но различное поведение), и f «s подпись верна, то это неправильно, и вы должен признать, что вызов f("foobar") не имеет смысла.