键入不匹配错误。 F#类型推断失败?

时间:2010-08-20 16:01:09

标签: .net f# functional-programming type-inference f#-interactive

我正在尝试在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#这样的子类转换为父类型吗?

6 个答案:

答案 0 :(得分:6)

您的最新代码几乎可以正常运行,但在这种情况下,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"

答案 1 :(得分:4)

您无法为函数返回值提供通用参数这一事实是一个红色标记:

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

getNewIndexForValue函数必须为'a参数选择一种类型; 'a类型参数可以是string,int,float或bool,但不能同时使用。{/ p>

一种可能性是引入AttributeIndex继承的非通用AttributeIndex<'a>类,并从函数返回普通AttributeIndex。 (使代码片段编译的最简单的更改是返回obj,尽管我认为这不会是永久修复。)

答案 2 :(得分: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>>

答案 3 :(得分:2)

我最初将此作为评论发布,但事实上它是'答案':

  

你打算做什么?   'someIndexes'稍后?

在你指明之前,任何'回答'都只是空闲的推测。人们无法提供关于如何克服这种“类型错误”的规定性建议,因为根据您最终打算如何使用数据,有许多可能的方法可以解决这个问题。我认为@JaredPar的答案最有可能是你想要的,但这基本上是我试图成为通灵者(以及其他所有答案)。

一旦你为每个索引指定了“你想做什么”,那么这将意味着所有索引需要支持的通用接口或基类,这将是你的答案。

答案 4 :(得分:2)

其他人都是对的,但我想我可以补充一点解释。

F#中的每个表达式都有一个类型。在F#中,“如果a then b else c”是表达式,则返回b或c。为此,b和c必须具有相同的类型。 “match ...”也是一个表达式,返回其中一个子句的值。这意味着每个子句必须具有相同的类型。

您的匹配表达式试图违反此规定。每个子句都有不同的类型。在您的情况下,这些是相同泛型类型的不同应用程序......但这些仍然是不同类型。这与尝试让一个子句返回一个int而另一个子句返回一个字符串......没有什么不同。

从概念上讲,这是问题的根源。在修复匹配表达式之前,您将无法定义返回其值的函数。

正如其他人所指出的,修复此问题的方法之一是为匹配表达式的类型使用公共基类型,例如obj或非泛型AttributeIndex。

答案 5 :(得分:0)

让我们来看看这一行:

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

AttributeIndex<_>中的下划线对编译器意味着:嘿,找出要插入的类型。它基本上只是节省了一些按键。如果您返回AttributeIndex<string>,编译器将推断_分别为stringboolint

你做的是在这里返回不同的类型。这不是某种类型,而是允许的任何类型。这与Java的通用通配符基本不同。

  • Java List<?>任何可能值的列表(如object所做的那样)。

  • F#的_ list是一个类型的列表。

你想要的是Java通配符,在.NET下,你通过co / contravariant类型来表达。

因此,您需要AttributeIndex<obj>