Строка рассматривается как моноид

#scala #scalaz

#scala #scalaz

Вопрос:

Учитывая подпись, подобную этой или той:

 def foo[A, F[_]](implicit mon: Monoid[F[A]], pr: Pure[F]): F[A]
  

Предполагая, что A есть Char , есть ли способ получить a String вместо a List[Char] ?

String не принимает параметр типа, поэтому я предполагаю, что это невозможно. Какой следующий лучший вариант? Прямо сейчас я использую mkString результат, но это не кажется оптимальным.

Я думаю String , что это моноид с нулем "" и добавлением

Комментарии:

1. Зачем вам нужен моноид [F[A]] вместо просто моноида [F]?

2. @CheatEx, в данном случае я не тот, кто написал foo , я просто вызывающий.

Ответ №1:

Можно убедить String маскироваться под тип более высокого типа и, следовательно, допускать применение функций вида of foo . Однако вывод типа Scala в настоящее время не соответствует задаче вывода foo аргументов типа, поэтому вам придется указывать их явно,

 // Assuming the the definitions of Pure and Monoid from Scalaz

type ConstString = {
  type λ[X] = String
}

implicit def StringPure = new Pure[ConstString#λ] {
  def pure[A](a: => A) = a.toString
}

val sm = implicitly[Monoid[String]]
val sp = implicitly[Pure[ConstString#λ]]
val f : String = foo[Char, ConstString#λ](sm, sp) // OK
  

Обратите внимание, что аргумент Char типа to foo не используется и может быть любым, но должен быть чем-то: в этом случае либо Char является естественным выбором, но Nothing or Any также подойдет.

Обратите внимание, что это решение String учитывает особые характеристики: значения всех типов преобразуются в String s, поэтому pure[A](a : => A) : String оно реализуемо для всех типов A . Репликация этой идиомы для типов, отличных от String , скорее всего, должна была бы использовать какой-то механизм для реализации случаев, зависящих от типа, в теле pure (например, какое-то совпадение с шаблоном).

Комментарии:

1. Но implicitly[Pure[ConstString#λ]].pure('a') вернет пустую строку, так что это не очень полезно. Так ли это?

2. Конечно, вам понадобится другое определение Pure[ConstString#λ] для выполнения любой полезной работы. Ответ отредактирован, чтобы отразить это.

3. @DanielC.Sobral Действительно, вы это сделали 🙂

4. Я бы предложил a.asInstanceOf[String] отразить тот факт, что StringPure это своего рода обман.

5. @retronym Да, я согласен, что это особый случай из String -за особых характеристик. Я отредактировал ответ, чтобы отразить это, но я думаю, что настаиваю на этом. asInstanceOf[String] — это немного OTT 😉

Ответ №2:

Лучшее решение, которое я могу придумать, — это определить неявное преобразование из List[Char] в String .

Ответ №3:

Ваш анализ того, что система типов scala отклонит String как не являющуюся «типом более высокого типа» * -> *, верен. То есть тип String не может быть присвоен F[_] для любого F. Вы могли бы попробовать (я не проверял это) неявные преобразования…

 def foo[A, F[_], That](implicit mon: Monoid[F[A]], pr: Pure[F], FA_Is_That: F[A] <%< That)
  

… но я подозреваю, что это будет не так полезно, потому что вам придется предоставлять свои собственные индивидуальные преобразования там, где это необходимо, а также потому, что производительность будет ужасной, если предположить, что это горячая часть кода.

Или, используя стандартную библиотеку, вы могли бы использовать CanBuildFrom машинное оборудование, но далеко не очевидно, насколько хорошо это будет сочетаться с классами типов в стиле scalaz.

 def foo[A, F[_], That](implicit mon: Monoid[F[A]], pr: Pure[F], b: CanBuildFrom[A, F[A], That]): That
  

В теле метода, конечно, вам нужно будет использовать конструктор для построения возвращаемого значения, в отличие от классов типов Monoid / Pure, что, как я подозреваю, делает их несколько избыточными.