Ayrat
я чот совсем тупой, да?
Ayrat
какие тут нахер завтипы
Doge
Ayrat
Ayrat
ну ты тоже указывал их руками!
Ayrat
если ты про let oneInt: int = ...
то ясен хер это писать не надо
Romɑn
Чото что мапает значение в контейнере
let f a b c = a + b + c // int -> int -> int -> int
let a, b, c = Some 1, Some 2, Some 3
// f a b c // не компилится
let af = Some f // Some (int -> int -> int -> int)
let bf = Option.apply af a //Some( int -> int -> int)
let cf = Option.apply bf b // Some (int -> int)
let df = Option.apply cf c //Some int
Romɑn
Андрей
Мне сложно
функтор это для чего есть Select, монада / TakeWhile, а аппликатив это Aggregate
Romɑn
Romɑn
фуу
Doge
ну ты тоже указывал их руками!
В методе example никаких дженерик параметров у меня нет. И компилятор это разруливает сам.
Но ладно, чуть другой пример:
trait Parent { type Child }
trait DependentPair {
val x: Parent
val dependsOnX: x.Child
}
Doge
Это самый настоящий сигма тип
Ayrat
Doge
Для настоящих зависимых типов ещё надо уметь выражать пи тип (а вот тут нам имплиситы помогут):
trait Pi[T] { type U }
def dependent[T](t: T)(implicit pi: Pi[T]): pi.U = ???
Doge
Vladislav
Doge
смысл ни на каплю не поменялся. Вообще ни на грам
Ок, чуть другой пример, который через дженерики не получить:
val one: Parent = ???
val two: Parent = ???
val three: Parent = ???
// скопилится, т.к. у этого инстанса точно один и тот же тип:
implicitly[one.Child =:= one.Child]
// не скомпилится, т.к. эти типы для компилятора разные
implicitly[two.Child =:= three.Child]
implicitly[one.Child =:= two.Child]
Romɑn
Romɑn
я не очень понял
Ayrat
круто, но где это использовать?
хз
type Parent<'child>() = class end
type DependentPair<'a> =
abstract member x: Parent<'a>
abstract member dependsOn: 'a
Ayrat
так и не понял нихуя я
Doge
Ayrat
Ayrat
и слово приходится тут неуместо. ты пишешь x.Child
Ayrat
я пишу <'a>
Doge
тайп инфиренс спрячет
Проблемы начнутся, когда ты будешь с такими типами писать методы и тебе придется повсюду пропихивать дженерик параметры
Ayrat
Ayrat
в скале - да
Doge
Не в коде вызывающем, а в определении самих методов
Doge
В структурах, которые будут использовать эту структуру и т.д.
Ayrat
ну определил раз и всё, даже меньше чем у тебя получилось
Ayrat
Ещё раз, напиши код, который сломается на языке с дженериками
Ayrat
или будет выглядеть вырвиглазно
Ayrat
пока что он выглядит точно так же и нихуя не ломается
Ayrat
но на языке с дженериками надо писать дженерик тип, а на языке без дженериков надо писать
type.Type
Doge
Вот он ломается.
Ayrat
то есть я примерно нихуя не понял
Doge
Romɑn
Donnie
Doge
я честно сказать не совсем понял что там написано. Я даже не в курсе что делает оператор =:=
Не смог найти минимального и простого примера, иду по хардкору:
Ну да, тут основная разница в том, что у тебя у инстансов одного типа (Parent) оказываются разные внутренние типы (instanceOne.Child) и (instanceTwo.Child).
Если этот факт и type projection не использовать, то да, можно всегда заменить дженериками (пусть и вербознее будет в дженерик методах, структурах и т.д.)
Если добавить при этом type projection, то тут открываются приколы повеселее:
trait Nat {
type Add[A <: Nat] <: Nat
type Mult[A <: Nat] <: Nat
}
class _0 extends Nat {
type Add[A <: Nat] = A
type Mult[A <: Nat] = _0
}
class S[N <: Nat] extends Nat {
type Add[A <: Nat] = S[N#Add[A]]
type Mult[A <: Nat] = A#Add[A#Mult[N]]
}
type +[A <: Nat, B <: Nat] = A#Add[B]
type *[A <: Nat, B <: Nat] = A#Mult[B]
type _1 = S[_0]
type _2 = S[_1]
type _3 = S[_2]
type _4 = S[_3]
type _5 = S[_4]
type _6 = S[_5]
type _7 = S[_6]
type _8 = S[_7]
type _9 = S[_8]
type _10 = S[_9]
type _11 = S[_10]
implicitly[_3 =:= _1 + _2]
implicitly[_4 =:= _2 * _2]
Doge
Увы, не нашел на своем компе более минимального примера логики на type projection, так что пришлось стащить стандратную пеановскую арифметику
Doge
Такое просто на дженериках уже никак не сделать
Ayrat
Ayrat
но конкретно в фшарпе нет хкт
Ayrat
так что дело тут не в дженериках
Ayrat
Ayrat
но опять же, дальше тайп инфиренс выводить эту срань должен
Ayrat
но хкт нет, поэтому первый же трейт на фшарпе невыразим
type Nat<'add<'a>, mult<'a>> = ...
Doge
Ayrat
Doge
Надо чуть подумать.
Ayrat
а, чорт
Ayrat
проклятый хачкель
Doge
В хаскеле такое на тайпклассах делают
Doge
+ fundeps или type families
Doge
Скаловские type projection - это как раз некий аналог type families
Doge
Насколько я понимаю.
Doge
а, чорт
Но вопрос интересный. Надо будет попробовать твоим энкодингом на скале так сделать.
Ayrat
Doge
Тут, конечно, извиняюсь, из меня плохой рассказчик с самого начала был.
Надо было сразу явно разделить и рассказать про abstract types, path dependent types и type projection.
А я всё в кучу смешал
Doge
И само собой ничего не понятно было.
Doge
Но тут и скала все усложнила, потому что в расте и хаскеле, они себя скорее как type projection введут, а не как скаловские abstract types
Doge
Поэтому там их в общем случае через дженерик параметры выразить нельзя
Андрей
можно тоже попробую жести вкинуть ))
Андрей
Пример из туториала по миксинам
Есть трейт итератор
abstract class AbsIterator {
type T
def hasNext: Boolean
def next: T
}
Нам нужен foreach, это не ключевое слово, просто функция.
Можно расширить трейт без явного знания, что такое Т, но с использованием T, hasNext и next
trait ForEachIterator extends AbsIterator {
def foreach(f: T => Unit) { while (hasNext) f(next) }
}
а можно нашлепать еще кучу расширений, например итерацию по каждому второму
Каждый раз указывать тип T неудобно, особенно если он там не нужен, или их там больше одного
trait EverySecondIterator extends AbsIterator {
def everySecond(f: T => Unit) {
while (hasNext) {
next
if (hasNext) f(next)
}
}
}
Можно взять тип и определить трейт для типа Т, например String, независимо есть там foreach или нет. Тут пока как обычно
побуквенно
class CharsIterator(s: String) extends AbsIterator {
type T = Char
private var i = 0
def hasNext = i < s.length()
def next = { val ch = s charAt i; i += 1; ch }
}
или по-словно
class WordsIterator(s: String) extends AbsIterator {
type T = Char
private var i = 0
val words = s.split(' ')
def hasNext = i < words.length()
def next = { val word = words elemAt i; i += 1; word }
}
а теперь их можно комбинировать, хотя везде уже есть реализация с кодом - кейк паттерн так называемый
class WordsIter extends WordsIterator(args(0)) with ForEachIterator with EverySecondIterator
val words = new WordsIter
words foreach println
class CharsIter extends CharsIterator(args(0)) with ForEachIterator
val chars = new CharsIter
chars foreach println
такая петрушка используется в скалатесте,
там можно указать "матчеры" (у здорового человека "ассерты") по вкусу - expect, shouldbe, property итп
или структурой и наименованием тесткейсов управлять - плоско или вложенно
Ilya
Хм, столкнулся с ситуацией, где None -- это happy path, а Some нежелателен. Это ж нормально? А то непривычно как-то.