簡體   English   中英

鍵入不匹配錯誤。 F#類型推斷失敗?

[英]Type mismatch error. F# type inference fail?

我正在嘗試在F#中編寫一個方法,該方法根據傳遞給方法的值的類型返回泛型類型的新實例。 在FSI:

 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'

我似乎無法弄清楚如何根據傳遞給函數的value參數的類型,使用類型param獲取Attribute的實例。 我嘗試過其他幾種變體,但都會導致相同的類型不匹配錯誤。 任何幫助將不勝感激。 謝謝!!

更新:

謝謝你的回答。 我現在明白了。 所以現在我試圖讓我的'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#這樣的子類轉換為父類嗎?

您的最新代碼幾乎可以正常工作,但在這種情況下,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"

您無法為函數返回值提供通用參數的事實是一個紅色標記:

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

getNewIndexForValue函數必須為'a參數選擇一個類型; 'a type參數可以是string,int,float或bool,但不能同時使用。

一種可能性是引入AttributeIndex<'a>繼承的非泛型AttributeIndex類,並從函數返回一個普通的AttributeIndex (使代碼片段編譯的最簡單的更改是返回obj ,盡管我認為這不會是永久修復。)

問題是您正在嘗試獲取非泛型方法以返回不同的綁定泛型類型。 這是不可能的。 這相當於在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>>

我最初將此作為評論發布,但實際上它是“答案”:

你以后打算用'someIndexes'做什么?

在你指定之前,任何“回答”都只是空閑的推測。 人們無法提供關於如何克服這種“類型錯誤”的規定性建議,因為根據您最終打算如何使用數據,有許多可能的方法可以解決這個問題。 我認為@JaredPar的答案最有可能是你想要的,但這基本上是我試圖成為通靈者(以及其他所有答案)。

一旦您為每個索引指定了“您想要做什么”,那么這將意味着所有索引都需要支持的通用接口或基類,這將是您的答案。

其他人都是對的,但我想我可以補充一點解釋。

F#中的每個表達式都有一個類型。 在F#中,“如果a then b else c”是表達式,則返回b或c。 為此,b和c必須具有相同的類型。 “match ...”也是一個表達式,返回其中一個子句的值。 這意味着每個子句必須具有相同的類型。

您的匹配表達式嘗試違反此規則。 每個子句都有不同的類型。 在您的情況下,這些是相同泛型類型的不同應用程序......但這些仍然是不同類型。 這與嘗試讓一個子句返回一個int而另一個子句返回一個字符串沒什么不同...

從概念上講,這是問題的根源。 在修復匹配表達式之前,您將無法定義返回其值的函數。

正如其他人所指出的,解決此問題的方法之一是為匹配表達式的類型使用公共基類型,例如obj或非泛型AttributeIndex。

我們來看看這一行:

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

AttributeIndex<_>的下划線表示編譯器: 嘿,找出要插入的類型 它基本上只是節省了一些按鍵。 如果返回AttributeIndex<string> ,編譯器將分別推斷_string ,或boolint

你做的是在這里返回不同的類型。 它不是某種類型,而是任何類型都允許。 這與Java的通用通配符基本不同。

  • Java的List<?>任何可能值的列表(就像object一樣)。

  • F#的_ list是一個類型的列表。

你想要的是Java通配符,在.NET下,你通過co / contravariant類型來表達它。

因此,您需要AttributeIndex<obj>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM