J>ни одну из аннотаций типов нельзя убрать. Это нормально??
Вполне
На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...
Однако всё равно реально избавиться от аннотаций:
open System.Collections.Generic
type SchemePointBase() = class end
type SchemePoint() = inherit SchemePointBase()
type ISchemeGlyph =
abstract member Points : SchemePointBase seq
type SchemeItemList<'T>() = inherit List<'T>()
type PointList() = inherit SchemeItemList<SchemePoint>()
type GlyphList() = inherit SchemeItemList<ISchemeGlyph>()
type Scheme() =
member this.Glyphs = GlyphList()
member this.Points = PointList()
let scheme = Scheme()
let points = seq {
yield! scheme.Points |> Seq.cast<_>
yield! scheme.Glyphs |> Seq.collect (fun g -> g.Points)
}
Здравствуйте, Пельмешко, Вы писали:
П>На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...
ну не знаю. какие сложности там возникают, что вот так компилится
Здравствуйте, Jack128, Вы писали:
J>Здравствуйте, Пельмешко, Вы писали:
П>>На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...
J>ну не знаю. какие сложности там возникают, что вот так компилится
J>let points = seq { J> yield! scheme.Points |> Seq.cast<_> J> yield! scheme.Glyphs |> Seq.collect (fun glyph -> glyph.Points) J> }
J>а вот так:
J>let points = seq { J> yield! scheme.Points |> Seq.cast<_> J> yield! Seq.collect (fun glyph -> glyph.Points) scheme.Glyphs J> }
J>нет.
Это всё потому, что в ML языках "нет" функций нескольких переменных, и вызов функции имеет семантику последовательности частичных применений. В 1-м случае мы имеем:
scheme.Glyphs |> Seq.collect (fun glyph -> glyph.Points)
(* что эквивалентно *)
(|>) scheme.Glyphs (Seq.collect (fun glyph -> glyph.Points))
(* что, в свою очередь, эквивалентно *)
((|>) scheme.Glyphs) (Seq.collect (fun glyph -> glyph.Points))
Частичное применение '((|>) scheme.Glyphs)' задаёт ограничение на тип 2-го аргумента, что позволяет вывести тип выражения '(fun glyph -> glyph.Points)'.
Здравствуйте, k.o., Вы писали:
KO>Это всё потому, что в ML языках "нет" функций нескольких переменных, и вызов функции имеет семантику последовательности частичных применений. В 1-м случае мы имеем: KO>
KO>scheme.Glyphs |> Seq.collect (fun glyph -> glyph.Points)
KO>(* что эквивалентно *)
KO>(|>) scheme.Glyphs (Seq.collect (fun glyph -> glyph.Points))
KO>(* что, в свою очередь, эквивалентно *)
KO>((|>) scheme.Glyphs) (Seq.collect (fun glyph -> glyph.Points))
KO>
KO>Частичное применение '((|>) scheme.Glyphs)' задаёт ограничение на тип 2-го аргумента, что позволяет вывести тип выражения '(fun glyph -> glyph.Points)'.
KO>Во 2-м случае KO>
KO>Поскольку, для '(Seq.collect (fun glyph -> glyph.Points))' этого ограничения ещё нет, получаем ошибку вывода типов.
KO>Отсюда видно, что это можно исправить и изменив порядок следования аргументов Seq.collect, например: KO>
KO>let flip f x y = f y x
KO>flip Seq.collect (fun glyph -> glyph.Points) scheme.Glyphs
KO>
Здравствуйте, Jack128, Вы писали:
J>а почему они должны выводится после??
J>let inline add x y = x + y
J>let result = add a b J>чтобы понять можно так писать или нет — компилятору сначала нужно вычислить тип переменных a и b.
Для меня это раньше было неочевидным. До поста k.o.
Здравствуйте, Пельмешко, Вы писали:
П>Вполне
П>На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...
Мил человек. Где же ты здесь узрел хотя бы один ко-/контравариантный интерфейс?
А если их нет, то о чем ты спичеь ведешь?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, Пельмешко, Вы писали:
П>>Вполне
П>>На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...
VD>Мил человек. Где же ты здесь узрел хотя бы один ко-/контравариантный интерфейс?
Преобразование из seq<SchemePoint> в seq<SchemePointBase> разве не является проявлением ковариантности?
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, k.o., Вы писали:
KO>>Преобразование из seq<SchemePoint> в seq<SchemePointBase> разве не является проявлением ковариантности?
VD>Ага, если сам seq коварнитный. А так нет.
type seq<'T> = System.Collections.Generic.IEnumerable<'T>
public interface IEnumerable<out T> : IEnumerable
Вроде как, ковариантный, нет? Впрочем, если посмотреть на определение ковариантности здесь, то мы увидим, что, по крайней мере, в одной трактовке, достаточно того, что можно привести тип seq<SchemePoint> к типу seq<SchemePointBase>.
почему я обязан сначала привести к seq<SchemePoint> ?? Это что за изврат то?? Тот же C# спокойно хавает:
class Program
{
class Base { }
class Child : Base { }
class Other { }
static void Main(string[] args)
{
List<Child> childList = new List<Child>();
IEnumerable<Base> baseEnum = (IEnumerable<Base>)childList;
}
}
Здравствуйте, VladD2, Вы писали:
VD>Здравствуйте, k.o., Вы писали:
KO>>Преобразование из seq<SchemePoint> в seq<SchemePointBase> разве не является проявлением ковариантности?
VD>Ага, если сам seq коварнитный. А так нет.
Некоторые давно живут в .NET 4.0
Хотя зря я упомянул ковариантность, в F# её проявлений практически нету
Здравствуйте, Jack128, Вы писали:
J>почему я обязан сначала привести к seq<SchemePoint> ?? Это что за изврат то?? Тот же C# спокойно хавает:
Что за логика "раз в C# есть, то и в F# должно быть", ну разные языки же с разными корнями.
Вы не замечали, что в спецификации F# ВООБЩЕ нету упоминания такой штуки, как implicit conversion?
Это не наталкивает Вас на некоторые мысли относительно того, что именно Вы "обязаны" явно писать в F#?
А речь и 4-ом фрэймворке? Если, да то — да, коваринтен. Если речь о 3.5, то — нет.
Если F# не смог вывести типы в 4-ом фрэймворке, то это или баг в алгоритме вывода типов, или (что более вероятно) недостаток этого самого алгоритма.
Специально создал на Nemerle аналогичный пример использующий коваринтный интерфейс. Так как немерл пока что живет под 3.5 фрэймворком, плишлось описать сами интерфейс и реализовать методы-расширения с ним рабатающие (аналогичные тем, что есть в стандартной библиотеке немерла, но работающих с IEnumerable):
using Nemerle.Collections;
using System;
using System.Collections.Generic;
class SchemePointBase { }
class SchemePoint : SchemePointBase { }
public interface IEnumerable2[+T] { GetEnumerator() : object; }
interface ISchemeGlyph { Points : IEnumerable2[SchemePointBase] { get } }
class SchemeItemList[T] : IEnumerable2[T] { public GetEnumerator() : object { null } }
class PointList : SchemeItemList[SchemePoint] {}
class GlyphList : SchemeItemList[ISchemeGlyph] {}
class Scheme
{
public Glyphs : GlyphList { get; set; }
public Points : PointList { get; set; }
}
public module Program
{
public static Concat[TSource](this _first : IEnumerable2[TSource], _second : IEnumerable2[TSource]) : IEnumerable2[TSource]
{
throw NotImplementedException()
}
public MapLazy[From, To](this _source : IEnumerable2[From], _convert : From -> To) : IEnumerable2[To]
{
throw NotImplementedException()
}
public Flatten[TEnu, TOut](this _source : IEnumerable2[TEnu]) : IEnumerable2[TOut]
where TEnu : IEnumerable2[TOut]
{
throw NotImplementedException()
}
Main() : void
{
def scheme = Scheme();
_ = scheme.Points.Concat(scheme.Glyphs.MapLazy(_.Points).Flatten());
}
}
Пример, отлично компилируется и не требует ручного приведения типов.
KO> Впрочем, если посмотреть на определение ковариантности здесь, то мы увидим, что, по крайней мере, в одной трактовке, достаточно того, что можно привести тип seq<SchemePoint> к типу seq<SchemePointBase>.
А без поддержки ковариантности для seq (то бишь для IEnumerable[T]) привести их нельзя. Так что все ОК, с определениями.
Просто я думал, что речь идет о 3.5 фрэймворке где IEnumerable[T] не ковариантен.
А так да, это или недоработка, или баг в алгоритме вывода типов F#-а.
Ну, да всегда можно пользоваться более продвинутыми языками... ну вы поняли какими.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.