[英]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
,或bool
或int
。
你做的是在這里返回不同的類型。 它不是某種類型,而是任何類型都允許。 這與Java的通用通配符基本不同。
Java的List<?>
是任何可能值的列表(就像object
一樣)。
F#的_ list
是一個類型的列表。
你想要的是Java通配符,在.NET下,你通過co / contravariant類型來表達它。
因此,您需要AttributeIndex<obj>
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.