2

Я пытаюсь написать метод в F #, который возвращает новый экземпляр родового типа, основанный на типе значения, переданного в метод. В FSI:Ошибка несоответствия типа. Ошибка ввода типа F #?

open System.Collections.Generic 

type AttributeIndex<'a>() = 
    inherit SortedDictionary<'a, HashSet<int array>>() 

let getNewIndexForValue (value: obj) : AttributeIndex<_> = 
    match value with 
     | :? string -> new AttributeIndex<string>() 
     | :? int -> new AttributeIndex<int>() 
     | :? float -> new AttributeIndex<float>() 
     | :? bool -> new AttributeIndex<bool>() 
     | _ -> failwith "bad value type" 

let someIndexes = [ 
    getNewIndexForValue 9; 
    getNewIndexForValue "testString"; 
    getNewIndexForValue false; 
    getNewIndexForValue 5.67; 
] 

someIndexes;; 

Это не компилируется с ошибкой

error FS0001: Type mismatch. Expecting a AttributeIndex<string>
but given a AttributeIndex<int>
The type 'string' does not match the type 'int'

Я не могу показаться, чтобы выяснить, как получить экземпляр атрибута с типом парами на основе типа значение параметра передается в функцию. Я пробовал пару других вариантов, но все они приводят к ошибке несоответствия такого же типа. Любая помощь будет принята с благодарностью. Благодаря!!

UPDATE:

Спасибо за ответы. Я получаю это сейчас. Итак, теперь я пытаюсь вернуть свой getNewIndexForValue не общий шаблон класса AttributeIndex. Я реализовал это в C# и компилирует и работает, как я ожидал:

using System; 
using System.Collections.Generic; 

namespace Example { 

    public class AttributeIndexBase : SortedDictionary<object, HashSet<int[]>> { } 

    public class AttributeIndex<T> : AttributeIndexBase { 
     public void AddToIndex(T indexValue, int[] recordKey) { 
      if (!this.ContainsKey(indexValue)) { 
       this.Add(indexValue, new HashSet<int[]> { recordKey }); 
      } 
      else { 
       this[indexValue].Add(recordKey); 
      } 
     } 
    } 

    class Program { 
     static int Main(string[] args) { 
      var intIdx = GetIndexForValue(32); 
      var boolIdx = GetIndexForValue(true); 
      var doubleIdx = GetIndexForValue(45.67); 
      var someIndexes = new List<AttributeIndexBase> { 
       intIdx, 
       boolIdx, 
       doubleIdx 
      }; 
      return 0; 
     } 

     static AttributeIndexBase GetIndexForValue(object value) { 
      switch (value.GetType().Name.ToLower()) { 
       case "int32" : 
        return new AttributeIndex<int>(); 
       case "single" : 
        return new AttributeIndex<float>(); 
       case "double" : 
        return new AttributeIndex<double>(); 
       case "boolean" : 
        return new AttributeIndex<bool>(); 
       default : 
        throw new ArgumentException("The type of the value param is not allowed", "value"); 
      } 
     } 
    } 
} 

Однако, пытаясь порт это F # не работает:

module example 

    open System 
    open System.Collections.Generic 

    type AttributeIndexBase() = 
     inherit SortedDictionary<obj, HashSet<int array>>() 

    type AttributeIndex<'a>() = 
     inherit AttributeIndexBase() 

    let getNewIndexForValueType (value: ValueType) : AttributeIndexBase = 
     match value with 
      | :? int -> new AttributeIndex<int>() 
      | :? float -> new AttributeIndex<float>() 
      | :? bool -> new AttributeIndex<bool>() 
      | _ -> failwith "bad value type" 

    let someIndexes = [ 
     getNewIndexForValueType 9; 
     getNewIndexForValueType false; 
     getNewIndexForValueType 5.67; 
    ] 

Это, мне кажется, будет довольно прямой порт (за исключением F # версии я сдерживая его просто ValueType), но я получаю ошибку:

error FS0001: This expression was expected to have type AttributeIndexBase
but here has type AttributeIndex<int>

ли F # на самом деле просто не поддерживает бросок от дочернего к родительскому типу, например, C#?

+1

Что вы собираетесь делать с «someIndexes» позже? – Brian

+2

Чтобы ответить на ваш последний вопрос, прочитайте «Типы объектов кавычек/Upcasting» в MSDN: http://msdn.microsoft.com/en-us/library/dd233220.aspx – Jason

ответ

5

Ваш последний код почти сработает, но в этом случае F # требует явного повышения до AttributeIndexBase. Есть как минимум два способа сделать это: вы можете использовать ключевое слово upcast, или вы можете использовать оператор преобразования :>.

Первый вариант будет выглядеть следующим образом:

let getNewIndexForValueType (value: ValueType) : AttributeIndexBase = 
    match value with 
    | :? int -> upcast new AttributeIndex<int>() 
    | :? float -> upcast new AttributeIndex<float>() 
    | :? bool -> upcast AttributeIndex<bool>() 
    | _ -> failwith "bad value type" 

Хотя второй будет выглядеть следующим образом:

let getNewIndexForValueType (value: ValueType) : AttributeIndexBase = 
    match value with 
    | :? int -> new AttributeIndex<int>() :> _ 
    | :? float -> new AttributeIndex<float>() :> _ 
    | :? bool -> new AttributeIndex<bool>() :> _ 
    | _ -> failwith "bad value type" 
+0

Kvb, спасибо! Я люблю тебя!! Оба этих решения работают как шарм. Теперь это кажется таким очевидным. – NathanD

+1

+1 для двух вещей, которые я раньше не видел: upcast или:> для подчеркивания. – Jason

4

Тот факт, что вы не можете прийти с общим параметром для возвращаемого значения функции красный флаг:

let getNewIndexForValue (value: obj) : AttributeIndex< ?? what goes here > = 

getNewIndexForValue функция должна выбрать тип параметра 'a; параметр типа 'a может быть строкой, int, float или bool, но не все одновременно.

Одна из возможностей заключается в том, чтобы ввести неосновный класс AttributeIndex, который наследует и возвращает AttributeIndex. (Самое простое изменение, которое сделает компиляцию кода кода, будет возвращать obj, хотя я предполагаю, что это не будет постоянным исправлением.)

+0

Спасибо Тим, ДжаредПар, Джейсон и Дарио: I теперь понимаю. Теперь определенно имеет смысл теперь, что я сделал в C# и должен был использовать базовый AttributeIndex. Однако, когда я снова пытаюсь сделать то же самое в F #, он все равно не компилируется. Я обновил свой вопрос своими выводами и новыми примерами. Спасибо всем за вашу помощь !! – NathanD

2

Проблема в том, что вы пытаетесь получить не-общий метод для возврата различных связанные общие типы. Это просто невозможно. Это эквивалентно попытке следующее в C#

?WhatGoesHere? Create(object source) { 
    if (source is int) { 
    return new AttributeIndex<int>((int)source); 
    } else if (source is string) { 
    return new AttributeIndex<string>((string)source); 
    } 
    ... 
} 

Действительно единственный допустимый тип, который может быть вставлен для ?WhatGoesHere? является object, потому что он должен быть дан конкретный тип.

Существует несколько способов улучшить этот опыт. Наиболее прямолинейным является добавление неосновного базового класса AttributeIndex и наследуют от этого значения AttributeIndex<T>.

type AttributeIndex = 
    member GetValue : object -> HashSet<int array> 

type AttributeIndex<'a> = 
    inherit AttributeIndex 
    inherit IDictionary<'a, HashSet<int array>> 
0

Давайте посмотрим на эту строку:

let getNewIndexForValue (value: obj) : AttributeIndex<_> = 

Подчеркивание в AttributeIndex<_> означает компилятором: Эй, выяснить, какой тип вставки. Это в основном просто экономит некоторые нажатия клавиш. Если вы вернете AttributeIndex<string>, компилятор выберет _: string, или bool или int соответственно.

Что вы делаете, здесь возвращаются разные типы. Это не о определенного типа, но любого типа разрешено. Это в основном отличается от общих подстановочных знаков Java.

  • List<?> в Java список любые возможные значения (как object будет делать).

  • F # _ list - это список точно одного типа.

Что вы хотите - это подстановочный шаблон Java, а в .NET вы выражаете его через типы co/contravariant.

Следовательно, вам необходимо AttributeIndex<obj>.

+3

Обратите внимание, что F # не поддерживает дисперсию, и даже если бы это было так, в .NET-дисперсии можно использовать только с интерфейсами и делегатами. – kvb

2

Я первоначально отправил это как комментарий, но на самом деле это «ответ»:

What do you intend to do with 'someIndexes' later?

Пока не указано, что любой «ответ» просто досужие домыслы. Невозможно предоставить предписывающий совет о том, как пройти эту «ошибку типа», потому что есть много возможных способов преодолеть это в зависимости от того, как вы в конечном итоге собираетесь использовать данные. Я думаю, что ответ @ JaredPar скорее всего будет тем, что вы хотите, но это в основном я пытаюсь быть психическим (наряду с любым другим ответом).

Как только вы укажете «что вы хотите делать» с каждым индексом, это будет означать общий интерфейс или базовый класс, которые должны поддерживать все индексы, и это будет ваш ответ.

+0

Hi Brian: Код, приведенный выше, является лишь небольшим примером, созданным из большего набора кода, который я уже написал. По существу, то, что я создаю, является универсальным менеджером индексов в памяти. Идея состоит в том, что вы передаете ей иерархию объектов с требованием, чтобы каждый отдельный объект в иерархии объектов имел поле «id». Затем я создаю индексы для каждой строки или valuetype в объекте. При запросе индексов вы возвращаете список int [] (представляющий поля «id» для каждого уровня). Я смог сделать это уже на C# с 3,8 миллионами гетерогенных объектов, каждый из которых имеет иерархию на 3 уровня. – NathanD

+0

Я хотел бы поделиться всем своим кодом, чтобы узнать, что вы думаете, и если бы я мог заниматься этим совершенно другим, более функциональным способом. Но, Stackoverflow, вероятно, не место для этого. Если вы дадите мне средство для получения моего кода, я отправлю его. – NathanD

2

Все остальные права, но я думаю, что могу добавить немного больше объяснений.

Каждое выражение в F # имеет тип. В F # «если a тогда b else c» является выражением, возвращающим либо b, либо c. Чтобы это сработало, b и c должны иметь один и тот же тип. «match ...» также является выражением, возвращающим значение одного из его предложений. Это означает, что каждое предложение должно иметь один и тот же тип.

Ваше выражение выражения пытается нарушить это. Каждое предложение имеет другой тип. В вашем случае это разные приложения одного и того же типа ... но это все еще разные типы. Это ничем не отличается от попытки вернуть одно предложение int, а другое возвращает строку ...

Понятно, что это корень вашей проблемы. Пока вы не исправите выражение соответствия, вы не сможете определить функцию, которая возвращает свое значение.

Как указывали другие, одним из способов исправить это является использование общего базового типа для типа выражения соответствия, например obj или не общего атрибута AttributeIndex.

Смежные вопросы