2013-03-25 13 views
22

Jestem dość nowy scalaz i próbuję dowiedzieć się, dlaczego następujący kod działa:Dlaczego lista jest półgrupą, ale w Seq nie jest?

import scalaz._ 
import Scalaz._ 
scala> Map[String,List[String]]() |+| Map[String,List[String]]() 
res3: scala.collection.immutable.Map[String,List[String]] = Map() 

ale to nie ...

import scalaz._ 
import Scalaz._ 
scala> Map[String,Seq[String]]() |+| Map[String,Seq[String]]() 
<console>:14: error: value |+| is not a member of  scala.collection.immutable.Map[String,Seq[String]] 
      Map[String,Seq[String]]() |+| Map[String,Seq[String]]() 

widzę Mapa niejawny dla Półgrupa, ale nie widzę tej dla Listy lub Sekw.

kilka pytań:

  1. Gdzie jest niejawny dla ListSemigroup?
  2. Dlaczego nie ma jednego dla Seq?
+0

Jakiej wersji używasz? Twoje tagi sugerują, pytasz o scalaz-siedem, podczas gdy link do Semigroup.scala prowadzi do mastera, który jest 6.x. – folone

+0

Rzeczywiście używam 7. Naprawię mój link. – coltfred

Odpowiedz

27

Tak, w Scalaz 7 jest implicit List to Monoid function, który daje z powrotem Monoid[List[A]]. Monoid rozszerza SemiGroup, więc mamy listę objętych.

Seq nie otrzymuje tego specjalnego traktowania. Nie ma żadnej niejawnej konwersji z Seq do lub Semigroup. Jest implicit IndexedSeq to Monoid, ale to nam nie pomaga.

Dlaczego nie ma jednego dla Seq? Nie wiem Być może Seq narusza pewne prawa monoidżetów/półgrup, więc nie ma konwersji. Wydaje się, że w Scalaz 6 były problemy z SEQ więc usunęliśmy kilka funkcji: https://groups.google.com/forum/?fromgroups=#!searchin/scalaz/Seq/scalaz/Deaec1H11W4/gYFSquXjTzYJ

UPDATE

patrząc na doc scala staje się bardziej oczywiste, dlaczego ludzie scalaz poszedł tą drogą. List dziedziczy LinearSeq, który dziedziczy Seq. IndexedSeq dziedziczy Seq. Jeśli mieliby dostarczyć półgrupę dla Seq, mogłaby ona zastąpić każdą inną półgrupę na IndexedSeq lub LinearSeq i luźne zalety wydajności między tymi dwoma. Jeśli spojrzeć na podpisami scalaz dla append widać, że korzystają oni z tych różnic wydajności:

https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/std/List.scala

implicit def listMonoid[A]: Monoid[List[A]] = new Monoid[List[A]] { 
    def append(f1: List[A], f2: => List[A]) = f1 ::: f2 
    def zero: List[A] = Nil 
    } 

https://github.com/scalaz/scalaz/blob/scalaz-seven/core/src/main/scala/scalaz/std/IndexedSeq.scala

implicit def ixSqMonoid[A]: Monoid[IxSq[A]] = new Monoid[IxSq[A]] { 
    def append(f1: IxSq[A], f2: => IxSq[A]) = f1 ++ f2 
    def zero: IxSq[A] = empty 
    } 

jeśli będziemy kopać głębiej, widzimy, że sekwencyjny implementuje tylko ++, który ma gorszą wydajność na listach niż ::: dla operacji dołączania. Tak więc, aby odpowiedzieć na twoje drugie pytanie, wydajność. Jeśli skalaz zaimplementował półgrupę dla Seq, najprawdopodobniej doprowadziłoby to do niejednoznacznej wydajności, ponieważ byłbyś w stanie zoptymalizować tylko pod kątem indeksowania. Iterable ma ten sam problem.

+0

Świetna odpowiedź na pytanie o to, gdzie jest lista. Wygląda na to, że wszystkie iterale powinny być co najmniej półgrupami ... – coltfred

+0

Jeśli spojrzeć na dokumentację scala, Seq jest podstawą dla LinearSeq i IndexedSeq, które mają różne różnice w wydajności. Lista dziedziczy LinearSeq, więc widzę, dlaczego musieli wybrać List i IndexedSeq zamiast Seq. Zobaczę, czy mogę zaktualizować to bardziej zwięźle. – Noah

+0

To ma całkiem sens, dzięki! – coltfred

Powiązane problemy