[F#] О выводе типов
От: Jack128  
Дата: 27.04.10 20:18
Оценка:
итак есть такой код на C#

class SchemePointBase {}
class SchemePoint : SchemePointBase {}

interface ISchemeGlyph
{
    IEnumerable<SchemePointBase> Points {get;}
}

class SchemeItemList<T> : IList<T> {} // базовый список

class PointList : SchemeItemList<SchemePoint> {}
class GlyphList : SchemeItemList<ISchemeGlyph> {}

class Scheme
{
   GlyphList Glyphs { get; }
   PointList Points { get; }
}



Пытаемся всё это дело на F# заюзать:


        let points = seq { 
            yield! scheme.Points :> seq<SchemePoint> :?> seq<SchemePointBase>
            yield! Seq.collect (fun (glyph: ISchemeGlyph) -> glyph.Points) scheme.Glyphs
            }


ни одну из аннотаций типов нельзя убрать. Это нормально??
Re: [F#] О выводе типов
От: Пельмешко Россия blog
Дата: 27.04.10 21:59
Оценка:
Здравствуйте, Jack128, Вы писали:

J>Пытаемся всё это дело на F# заюзать:

J>
J>        let points = seq { 
J>            yield! scheme.Points :> seq<SchemePoint> :?> seq<SchemePointBase>
J>            yield! Seq.collect (fun (glyph: ISchemeGlyph) -> glyph.Points) scheme.Glyphs
J>            }
J>


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)
  }
Re[2]: [F#] О выводе типов
От: Jack128  
Дата: 28.04.10 06:06
Оценка:
Здравствуйте, Пельмешко, Вы писали:

П>На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...


ну не знаю. какие сложности там возникают, что вот так компилится

let points = seq {
yield! scheme.Points |> Seq.cast<_>
yield! scheme.Glyphs |> Seq.collect (fun glyph -> glyph.Points)
}

а вот так:

let points = seq {
yield! scheme.Points |> Seq.cast<_>
yield! Seq.collect (fun glyph -> glyph.Points) scheme.Glyphs
}

нет.
Re: [F#] О выводе типов
От: mryau Россия http://woodland.ru/~wjdogs
Дата: 28.04.10 10:11
Оценка:
Здравствуйте, Jack128, Вы писали:

J>
J>        let points = seq { 
J>            yield! scheme.Points :> seq<SchemePoint> :?> seq<SchemePointBase>
J>            yield! Seq.collect (fun (glyph: ISchemeGlyph) -> glyph.Points) scheme.Glyphs
J>            }
J>


J>ни одну из аннотаций типов нельзя убрать. Это нормально??


А так почему не нравится?

        let points = seq { 
            for i in scheme.Points do yield i
            for i in scheme.Glyphs do yield i
            }
f#
Re[2]: [F#] О выводе типов
От: mryau Россия http://woodland.ru/~wjdogs
Дата: 28.04.10 10:19
Оценка:
Не то написал в предыдущем письме

        let points = seq { 
            for i in scheme.Points do yield i
            for xs in scheme.Glyphs do for i in xs do yield i
            }
f#
Re[3]: [F#] О выводе типов
От: dsorokin Россия  
Дата: 28.04.10 10:30
Оценка: +1 -1
Здравствуйте, Jack128, Вы писали:

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>нет.


Напиши баг репорт. У меня, например, один баг приняли.
Re[3]: [F#] О выводе типов
От: k.o. Россия  
Дата: 28.04.10 11:43
Оценка: 12 (1)
Здравствуйте, 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)'.

Во 2-м случае
Seq.collect (fun glyph -> glyph.Points) scheme.Glyphs
(* эквивалентно *)
(Seq.collect (fun glyph -> glyph.Points)) scheme.Glyphs

Поскольку, для '(Seq.collect (fun glyph -> glyph.Points))' этого ограничения ещё нет, получаем ошибку вывода типов.

Отсюда видно, что это можно исправить и изменив порядок следования аргументов Seq.collect, например:
let flip f x y = f y x
flip Seq.collect (fun glyph -> glyph.Points) scheme.Glyphs
Re[4]: [F#] О выводе типов
От: Jack128  
Дата: 28.04.10 11:56
Оценка:
Здравствуйте, k.o., Вы писали:


KO>Отсюда видно, что это можно исправить и изменив порядок следования аргументов Seq.collect, например:

KO>
KO>let flip f x y = f y x
KO>flip Seq.collect (fun glyph -> glyph.Points) scheme.Glyphs
KO>



Угу. Либо просто оператор |> юзать. Сенкс. Интересно.
Re[4]: [F#] О выводе типов
От: dsorokin Россия  
Дата: 28.04.10 11:56
Оценка:
Здравствуйте, 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) scheme.Glyphs
KO>(* эквивалентно *)
KO>(Seq.collect (fun glyph -> glyph.Points)) scheme.Glyphs
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>


Интересно, что оператор (|>) инлайнится:

let inline (|>) x f = f x


Выходит, что типы выводятся до инлайна?
Re[3]: [F#] О выводе типов
От: Jack128  
Дата: 28.04.10 11:56
Оценка:
Здравствуйте, mryau, Вы писали:

M>Не то написал в предыдущем письме


M>
M>        let points = seq { 
M>            for i in scheme.Points do yield i
M>            for xs in scheme.Glyphs do for i in xs do yield i
M>            }
M>


Ну вопрос же не в том, как описать, чтоб не нуно было типы указывать, а почему в исходном коде их нуно указывать. Ну собственно КО объяснил.
Re[5]: [F#] О выводе типов
От: Jack128  
Дата: 28.04.10 12:00
Оценка:
Здравствуйте, dsorokin, Вы писали:

D>Здравствуйте, k.o., Вы писали:


D>Выходит, что типы выводятся до инлайна?


а почему они должны выводится после??

let inline add x y = x + y

let result = add a b
чтобы понять можно так писать или нет — компилятору сначала нужно вычислить тип переменных a и b.
Re[6]: [F#] О выводе типов
От: dsorokin Россия  
Дата: 28.04.10 12:23
Оценка:
Здравствуйте, Jack128, Вы писали:

J>а почему они должны выводится после??


J>let inline add x y = x + y


J>let result = add a b

J>чтобы понять можно так писать или нет — компилятору сначала нужно вычислить тип переменных a и b.

Для меня это раньше было неочевидным. До поста k.o.
Re[2]: [F#] О выводе типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.04.10 17:30
Оценка: -1
Здравствуйте, Пельмешко, Вы писали:

П>Вполне


П>На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...


Мил человек. Где же ты здесь узрел хотя бы один ко-/контравариантный интерфейс?

А если их нет, то о чем ты спичеь ведешь?
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[3]: [F#] О выводе типов
От: k.o. Россия  
Дата: 28.04.10 18:13
Оценка: +1
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, Пельмешко, Вы писали:


П>>Вполне


П>>На самом деле иерархии наследования, реализации интерфейсов и ко-/контравариантность создают определённые сложности для вывода типов, а у Вас тут как раз полный букет...


VD>Мил человек. Где же ты здесь узрел хотя бы один ко-/контравариантный интерфейс?


Преобразование из seq<SchemePoint> в seq<SchemePointBase> разве не является проявлением ковариантности?
Re[4]: [F#] О выводе типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 28.04.10 18:47
Оценка:
Здравствуйте, k.o., Вы писали:

KO>Преобразование из seq<SchemePoint> в seq<SchemePointBase> разве не является проявлением ковариантности?


Ага, если сам seq коварнитный. А так нет.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[5]: [F#] О выводе типов
От: k.o. Россия  
Дата: 29.04.10 04:26
Оценка:
Здравствуйте, 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>.
Re: [F#] И немного о приведении типов
От: Jack128  
Дата: 29.04.10 06:47
Оценка:
Здравствуйте, Jack128, Вы писали:



let points = seq { 
    yield! scheme.Points :> 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;
        }
    }
Re[5]: [F#] О выводе типов
От: Пельмешко Россия blog
Дата: 29.04.10 12:21
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Здравствуйте, k.o., Вы писали:


KO>>Преобразование из seq<SchemePoint> в seq<SchemePointBase> разве не является проявлением ковариантности?


VD>Ага, если сам seq коварнитный. А так нет.


Некоторые давно живут в .NET 4.0
Хотя зря я упомянул ковариантность, в F# её проявлений практически нету
Re[2]: [F#] И немного о приведении типов
От: Пельмешко Россия blog
Дата: 29.04.10 12:23
Оценка: :)
Здравствуйте, Jack128, Вы писали:

J>почему я обязан сначала привести к seq<SchemePoint> ?? Это что за изврат то?? Тот же C# спокойно хавает:


Что за логика "раз в C# есть, то и в F# должно быть", ну разные языки же с разными корнями.

Вы не замечали, что в спецификации F# ВООБЩЕ нету упоминания такой штуки, как implicit conversion?
Это не наталкивает Вас на некоторые мысли относительно того, что именно Вы "обязаны" явно писать в F#?
Re[6]: [F#] О выводе типов
От: VladD2 Российская Империя www.nemerle.org
Дата: 29.04.10 12:23
Оценка:
Здравствуйте, k.o., Вы писали:

KO>
KO>type seq<'T> = System.Collections.Generic.IEnumerable<'T>
KO>

KO>
KO>public interface IEnumerable<out T> : IEnumerable
KO>

KO>Вроде как, ковариантный, нет?

А речь и 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#-а.

Ну, да всегда можно пользоваться более продвинутыми языками... ну вы поняли какими.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.