[Scala] не до конца понял концепцию ко- и контр- варативности
просто часть вариантов, расширяющих менее подробный тип до более подробного, никогда не будут сматчены. И тогда выходит, что PartialFunction должен быть по первому аргументу ковариантен. Почему скала настаивает на обратном?если использовать эту логику, то тогда в match, вообще, можно передавать любой тип. не находишь?
зы
обратная вариантность используется только для записи(присвоения)
match - обычное чтение
по смыслу, тип параметра передаваемого в match указывается самый базовый из тех, для которых этот match может использоваться
а уже внутри веток матча мы может указывать производные типы
отсюда и получается, что "меньший" тип мы передать не можем, в качестве, параметра, но можем передать "больший"
если использовать эту логику, то тогда в match, вообще, можно передавать любой тип. не находишь?не. Только из родительской ветки. В смысле от кого-либо из предков. Предки более узкие и обязательно заматчатся. Наследников передавать нельзя, потому что они более широкие и могут не заматчиться. Не связанные с нами типы вообще передавать бессмысленно, линейка матчеров для short,word,int никак не сработает для float,double
Наследников передавать нельзя, потому что они более широкие и могут не заматчиться.на какое правило (кроме указания явного типа) не может замапиться наследник?
на какое правило (кроме указания явного типа) не может замапиться наследник?Тип P: {a,b}
Тип C extends P: {a,b,c,d}
В скобочках - список значений (можно так же называть списком конструкторов)
Матчер для P:
{
case a => res1
case b => res2
}
Он не заматчит c или d
P.S. Я начинаю понимать, что, возможно, что-то с чем-то попутал
Он не заматчит c или dно это фактически отдельные значения, не замапятся, но и фиг с ними.
ситуация возникает хуже, если рассматривать матчинг полей типа.
если в матче используется матчинг поля, а мы передали предка, в котором этого поля нет, то непонятно что именно должен делать match (как минимум как match поймет, что поля нет?)
Причём ограничение на матчинг поля явно сильнее, потому что матчинг конструктора в скале - это фактически операция unapply, то есть тоже обращение к полю.
В таком случае, я думаю, мне стоит прямо спросить что нужно, и возможно мне кто-нибудь подскажет как это правильно формализировать.
Пишу я тестовую систему обмена сообщениями между акторами. Эти сообщения являются pojo типами и просто описывают команды. При наследовании список команд только расширяется. Что я думал сделать:
trait Protocol1
case object ob1 extends Protocol1
case class cl1 (someData : int) extends Protocol1
trait Protocol2
case object ob2 extends Protocol2
case class cl2 (someData : int) extends Protocol2
trait Protocol3 extends Protocol2 with Protocol1
Потом написать код, который будет это матчить и принимать соответствующие действия. Тут-то и всплыл вопрос правильной типизации и правильной параметризации. В моём варианте явно получается, что рисивер для Protocol3 может обработать и Protocol2 и Protocol1.
Но типичная точка зрения скаловской системы типов на наследовании говорит обратное.
также можно добавить промежуточный тип
например, Receiver описываем как получающего тип P1-3, и делаем неявные преобразования из типов P1, P2, P3 в P1-3 (в этом случае, преобразование может быть просто wrapper-ом, без конвертации одного протокола в другой)
может тогда стоит забить на типизацию и подставить везде AnyRef?
может тогда стоит забить на типизацию и подставить везде AnyRef?тогда уж Protocol1,
или Protocol - если считается, что может возникнуть ситуация, когда ProtocolN может быть не совместим с ProtocolN-1
Оставить комментарий
yroslavasako
Смотрю я на Function[-T1,R] и прекрасно понимаю откуда берётся -T1.Если T' наследник T, то Function[T'] не может быть наследником от Function[T], поскольку его нельзя вызвать вместо предполагаемого родителя, ведь он ожидает более подробный тип, в менее подробном ему может чего-нибудь фатально не хватить. Всё логично.
Но почему PartialFunction тоже имеет вид PartialFunction[-T,R], когда с ней по идее всё наоборот? PartialFunction - это правая часть match-выражения. А выражение, способное справиться с более подробным типом, точно также способно заматчить менее подробный, просто часть вариантов, расширяющих менее подробный тип до более подробного, никогда не будут сматчены. И тогда выходит, что PartialFunction должен быть по первому аргументу ковариантен. Почему скала настаивает на обратном?