Использование фильтра в shapeless, Scala

Легко отфильтровать HList в бесформенных по типу:

val hlist = 1 :: 2 :: "3" :: true :: false :: HNil
hlist.filter[Int]

Но как я могу создать фильтр собственного типа? Я хочу что-то вроде этого: например, я получил список некоторых функций:

def function1(s: String) = s.toInt
def function2(s: String) = s.toDouble
def function3(i: Int) = i.toDouble

val hflist = function1 _ :: function3 _ :: function2 _ :: HNil

hflist customFilter[String] //> function1 _ :: function2 _ :: HNil

Таким образом, после использования этого фильтра будет построен список функций из типа String в какой-либо другой тип.

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

ВЕРСИЯ

Подробнее о моем комментарии:

Я попытался проверить эти идеи на карте:

Итак, если у меня есть несколько списков (давайте поработаем с hlist и hflist):

object allFunction extends Poly1 {
  implicit def default[T, M] =
    at[T => M](t => { 
      object grabStringFunc extends skip {
        implicit def stringFunc[A] = at[T => A](_ :: HNil)
      }

      println(hflist flatMap grabStringFunc) //> here we should see result, list of functions 
 })

 hlist map allFunction
 //> result of this should be smth like (types)
 //> shapeless.::[Int => Double,shapeless.HNil]]
 //> shapeless.::[Int => Double,shapeless.HNil]]
 //> shapeless.::[String => Int,shapeless.::[String => Double,shapeless.HNil]] 
 //> shapeless.HNil
 //> shapeless.HNil

Очень интересно, почему он компилируется и работает некорректно? Как я думаю, это не работает, потому что объект не может принимать параметры типа таким образом...


person DaunnC    schedule 10.11.2013    source источник


Ответы (1)


Самый простой способ — использовать складку. Сначала нам нужна полиморфная функция, которая будет добавлять каждый элемент в аккумулятор, если он имеет нужный тип (String => A для некоторого A), и игнорировать его в противном случае:

trait ignore extends Poly2 {
  implicit def default[A, L <: HList] = at[A, L]((_, l) => l)
}

object keepStringFunc extends ignore {
  implicit def stringFunc[A, L <: HList] = at[String => A, L](_ :: _)
}

Теперь следующее даст результат, который вы хотите получить как в 1.2.4, так и в 2.0.0-M1:

val filtered = hflist.foldRight(HNil)(keepStringFunc)

Вы также можете написать свой собственный класс типов по модели Filter, FilterAux (или Filter.Aux) и т. д. — и это будет хорошим упражнением, если вы пытаетесь освоить Shapeless, — но foldRight намного проще.


Обновление: на самом деле, для чего это стоит, есть немного более краткий способ сделать это с помощью flatMap:

trait skip extends Poly1 {
  implicit def default[A] = at[A](_ => HNil)
}

object grabStringFunc extends skip {
  implicit def stringFunc[A] = at[String => A](_ :: HNil)
}

val filtered = hflist flatMap grabStringFunc

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


В ответ на ваш комментарий: вы можете сделать решение более общим:

trait skip extends Poly1 {
  implicit def default[A] = at[A](_ => HNil)
}

trait grabFuncFrom[T] extends skip {
  implicit def stringFunc[A] = at[T => A](_ :: HNil)
}

object grabStringFunc extends grabFuncFrom[String]

val filtered = hflist flatMap grabStringFunc

Но вам все равно понадобится этот последний шаг, когда вы создаете функцию более высокого ранга как объект (см., например, этот ответ и комментарий Майлза для некоторого обсуждения этого вопроса).

person Travis Brown    schedule 10.11.2013
comment
омг, такое красивое решение; большое спасибо! Я тоже попробую написать свой Filter. Спасибо! - person DaunnC; 10.11.2013
comment
Хм, небольшая редакция моего вопроса, можем ли мы передать параметр в функцию, я имею в виду что-то вроде def stringFunc[A] = at[T => A](_ :: HNil)? почему компилируется, и почему, собственно, не работает? p.s. Я на пути к написанию собственного Filter :) спасибо! - person DaunnC; 11.11.2013
comment
у, для понимания бесформенного. Хотел бы я много раз голосовать за ваши ответы. Я добавил некоторую информацию о своем комментарии, что я пробовал. Спасибо за ваше время! :) - person DaunnC; 11.11.2013