Можно ли `A =› List[B]` преобразовать в `List[A =› B]`?

Я пытаюсь найти реализацию этой сигнатуры функции Scala:

def explode[A, B](f: A => List[B]): List[A => B]

Возможно обратное направление:

def nest[A, B](fs: List[A => B]): A => List[B] = (a: A) => fs.map(_(a))

К настоящему времени я склонен полагать, что первый (explode) нереализуем, но я рад, что ошибся. Если это действительно невозможно реализовать, есть ли за этим глубокая причина?

На мой взгляд, я, по сути, прошу компилятор дублировать этот ввод A несколько n раз (размер Lists) и зафиксировать его как ввод.


person Eliav Lavi    schedule 26.12.2020    source источник
comment
@LuisMiguelMejíaSuárez См. здесь . (Кроме того, это не одна и та же функция, каждая дает разные B)   -  person Eliav Lavi    schedule 27.12.2020
comment
Да, прости. Я все же думаю, что стоило бы объяснить, что вы хотите сделать   -  person Luis Miguel Mejía Suárez    schedule 27.12.2020
comment
Почему вы не работаете со списками кортежей? Или карты? Что-то вроде этого: def explode[A, B](f: A => List[B])(a: A): List[(A, B)] = { f(a).map(b => a -> b) } ?   -  person Tomer Shetah    schedule 27.12.2020


Ответы (2)


На мой взгляд, я, по сути, прошу компилятор дублировать этот ввод A несколько n раз (размер Lists) и зафиксировать его как ввод.

Проблема в том, что вы не знаете, что такое n. Рассмотрим, например, функцию, которая возвращает список всех простых делителей числа:

def divisors(n: Int): List[Int] = ???

Что вы ожидаете от explode(divisors)? divisors может возвращать List любого размера, в зависимости от его аргумента, всякий раз, когда он вызывается в будущем. Но когда вы вызываете explode, он должен немедленно вернуть List фиксированного размера.

Учитывая фиксированный тип A, сигнатуры в вашем коде могут быть записаны следующим образом:

type F[T] = List[T]
type G[T] = A => T

def nest[B]: F[G[B]] => G[F[B]]
def explode[B]: G[F[B]] => F[G[B]]

nest и explode напоминают операцию sequence. Это работает для nest, потому что можно написать экземпляр Traverse для List, но невозможно написать экземпляр Traverse для функции A => T. Вот эквивалентный вопрос для Haskell, это дает больше понимания.

person Kolmar    schedule 26.12.2020
comment
Спасибо за ваш информативный ответ! Вы пишете: ...Но когда вы вызываете nest, он должен немедленно вернуть список фиксированного размера. (Я полагаю, вы имели в виду explode). И это правда. Я пытаюсь подумать, мог ли бы быть другой ответ, если бы я был готов отказаться от части, где список немедленно возвращается с фиксированным размером - что, если бы я работал с Stream / LazyList и т. д.? - person Eliav Lavi; 26.12.2020
comment
Я думаю, что Stream все же не годится, потому что его можно исчерпать сразу сразу, например. explode(divisors).toList. И List может быть пустым для некоторых n. Это теоретический вопрос, или у вас есть проблема, которую вы хотите решить? - person Kolmar; 26.12.2020
comment
Я понимаю что ты имеешь ввиду. Я думаю, что пустое List может работать нормально, но суть остается неизменной. Это в основном теоретически, но это связано с побочным проектом, который я пробую. Боюсь, давать более подробный контекст было бы слишком сложно и не поможет. По сути, это означает, что мне, вероятно, следует поискать другое направление в моделировании моей проблемы. - person Eliav Lavi; 27.12.2020

Если вы хотите сделать какую-то реализацию, которая просто удовлетворяет сигнатуре, вы можете сделать что-то вроде этого:

def explode[A, B](f: A => List[B]): List[A => B] = {
  Nil
}

def explode[A, B](f: A => List[B]): List[A => B] = {
  List(f.andThen(_.head))
}

Но я думаю, вы хотите что-то семантически другое:

продублируйте этот ввод A несколько раз (размер списков) и зафиксируйте его как ввод

В таком случае есть проблема. Результат f в общем случае зависит от ввода A. Это может быть Nil, список конечного размера или бесконечный список.

Что вы можете сделать, это что-то вроде:

def explode[A, B](f: A => List[B]): A => List[A => B] = {
  f.andThen(_.map(b => (_: A) => b)
}
person Artem Sokolov    schedule 26.12.2020