2016-02-19 2 views
0

Я реализовал государственную монаду, и я работаю над включением LINQ «Синтаксис понимания» с помощью методов расширения.Что вызывает ошибку «Тип выражения в предложении let неверен».

Может ли кто-нибудь объяснить причину ошибки в названии? Возможно, метод расширения отсутствует или у него неправильная подпись. Непонятно, что более подробные аспекты работы LINQ и что-то вроде тривиального, как предложение let.

Обращаем внимание: в других случаях подобные сообщения звучат в связи с предложением «выбрать» или «присоединиться»; Я не смог различить какое-либо сходство с моим примером ниже.

Этот пример компилируется и работает правильно:

namespace StateMonad { 
    using StateUnit = State<Unit,GCDState>; 
    using TupleUnit = State<Unit,GCDState>.StateTuple; 

    private static class TestClass { 

    // This usage compiles and runs fine. 
    private static readonly StateUnit GcdInner_Good = 
     (from s in State<Unit,GCDState>.Get 
     select new TupleUnit(Unit.unit, 
       s.A > s.B ? new GCDState(s.B, s.A-s.B) 
      : s.A < s.B ? new GCDState(s.B, s.A ) 
         : s) 
    ); 
// continued below 

Но это один не делает, генерируя ошибку интереса на первом let:

// continued from above 

    // This usage fails to compile, with error as shown 
    private static readonly StateUnit GcdInner_Bad = 
     (from s in State<Unit,GCDState>.Get 
     let A = s.A  // Generates error on "let": 
     let B = s.B  // "The type of the expression in the let clause is incorrect. 
          // Type inference failed in the call to 'Select'." 
     select new TupleUnit(Unit.unit, 
       A > B ? new GCDState(B, A - B) 
      : A < B ? new GCDState(B, A ) 
         : s) 
    ); 
    } 
} 

Вот методы расширения, объявленные до сих пор:

public static class StateExtensions { 
    [Pure]public static State<TResult,TState> Select<TValue,TState,TResult>(this 
     State<TValue,TState> @this, 
     Func<TValue,State<TResult,TState>.StateTuple> projector 
    ) where TResult:struct where TState:struct where TValue:struct { 
     projector.ContractedNotNull("projector"); 

     return new State<TResult,TState>(s => projector(@this.EvalState(s))); 
    } 

    [Pure]public static State<TResult,TState> SelectMany<TValue,TState,TResult>(this 
     State<TValue,TState> @this, 
     Func<TValue,State<TResult,TState>> selector 
    ) where TResult:struct where TState:struct where TValue:struct { 
     selector.ContractedNotNull("selector"); 

     return @this.Bind(selector); 
    } 

    [Pure]public static State<TResult,TState> SelectMany<TValue,TState,T,TResult>(this 
     State<TValue,TState> @this, 
     Func<TValue, State<T,TState>> selector, 
     Func<TValue, T, TResult> projector 
    ) where TResult:struct where TState:struct where TValue:struct where T:struct { 
     selector.ContractedNotNull("selector"); 
     projector.ContractedNotNull("projector"); 

     return new State<TResult, TState>(s => { 
      var value = @this.RunState(s).Value; 
      return new State<TResult, TState>.StateTuple(
       projector(value, selector(value).RunState(s).Value) ,s); 
     });  
    } 
    } 

И вот основные положения Государственная монада класс:

public struct State<TValue,TState> : IEquatable<State<TValue,TState>> 
    where TValue:struct where TState:struct { 
    public delegate StateTuple Transformer(TState state); 

    public State(Transformer transformer) : this() { 
     Contract.Requires(transformer != null); 
     _transformer = transformer; 
    } 

    [Pure]public State<TResult,TState> Bind<TResult> (
     Func<TValue, State<TResult,TState>> selector 
    ) where TResult:struct { 
     selector.ContractedNotNull("selector"); 

     var @this = this; 
     return new State<TResult,TState>(state => { 
      var tuple = @this.RunState(state); 
      return selector(tuple.Value).RunState(tuple.State); 
     }); 
    } 

    [Pure]public StateTuple RunState(TState state) { return _transformer(state); } 
    [Pure]public TValue  EvalState(TState state) { return RunState(state).Value; } 
    [Pure]public TState  ExecState(TState state) { return RunState(state).State; } 

    private readonly Transformer _transformer; 
    } 


public struct StateTuple { 
    public StateTuple(Tuple<TValue, TState> content) : this(content.Item1,content.Item2) { 
     content.ContractedNotNull("content"); 
    } 
    public StateTuple(TValue value, TState state) : this() { 
     _value = value; _state = state; 
    } 
    public TValue Value { get {return _value;} } readonly TValue _value; 
    public TState State { get {return _state;} } readonly TState _state; 

    #region Value Equality with IEquatable<T>. 
    /// <inheritdoc/> 
    [Pure]public override bool Equals(object obj) { 
    var other = obj as StateTuple?; 
    return other != null && other.Equals(obj); 
    } 

    /// <summary>Tests value-equality, returning <b>false</b> if either value doesn't exist.</summary> 
    [Pure]public bool Equals(StateTuple other) { 
    return this.Value.Equals(other.Value) && this.State.Equals(other.State); 
    } 

    /// <inheritdoc/> 
    [Pure]public override int GetHashCode() { unchecked { return Value.GetHashCode()^State.GetHashCode(); } } 

    /// <inheritdoc/> 
    [Pure]public override string ToString() { 
    Contract.Ensures(Contract.Result<string>() != null); 
    return String.Format("({0},{1})",Value,State); 
    } 

    /// <summary>Tests value-equality, returning <b>false</b> if either value doesn't exist.</summary> 
    [Pure]public static bool operator == (StateTuple lhs, StateTuple rhs) { return lhs.Equals(rhs); } 

    /// <summary>Tests value-inequality, returning <b>false</b> if either value doesn't exist..</summary> 
    [Pure]public static bool operator != (StateTuple lhs, StateTuple rhs) { return ! lhs.Equals(rhs); } 
    #endregion 
} 

Вот еще несколько утилит, необходимых для компиляции:

/// <summary>TODO</summary> 
    public static class State { 
    public static State<bool,TState>   DoWhile<TState>(this 
     State<bool,TState>.Transformer body 
    ) where TState:struct { 
     return new State<bool,TState>(state => { 
      State<bool,TState>.StateTuple tuple; 
      do { tuple = body(state); state = tuple.State; } while (tuple.Value); 
      return tuple; 
     }); 
    } 

    /// <summary>Implements the Haskell operator (liftM): liftM f m = m >>= (\x -> return (f x))</summary> 
    public static State<B,TState> LiftM<TState,A,B>(this 
     State<A,TState> @this, 
     Func<A,B> func 
    ) where TState:struct where A:struct where B:struct { 
     return @this.Bind(t => new State<B,TState>(s => new State<B,TState>.StateTuple(func(t),s))); 
    } 
    } 

    /// <summary>TODO</summary> 
    public static class State<TState> where TState:struct { 
    public readonly static State<TState, TState> Get 
     = new State<TState, TState>(s => new State<TState,TState>.StateTuple(s, s)); 
    public   static State<Unit,TState>  Put(TState state) { 
     return new State<Unit,TState>(s => new State<Unit,TState>.StateTuple(Unit.unit,state)); 
    } 

    #region Convenience extensions to Get() for efficiency 
    /// <summary>TODO</summary> 
    /// <typeparam name="TValue"></typeparam> 
    /// <typeparam name="TState"></typeparam> 
    /// <param name="selector"></param> 
    [Pure]public static State<TValue,TState> GetBind<TValue>(
     Func<TState, State<TValue,TState>> selector 
    ) where TValue:struct { 
     selector.ContractedNotNull("selector"); 

     return new State<TValue,TState>(s => selector(s).RunState(s)); 
    } 

    /// <summary>TODO</summary> 
    /// <typeparam name="TValue"></typeparam> 
    /// <typeparam name="TState"></typeparam> 
    /// <param name="selector"></param> 
    [Pure]public static State<TValue,TState> GetCompose<TValue>(
     Func<TState,State<TValue,TState>> selector 
    ) where TValue:struct { 
     selector.ContractedNotNull("selector"); 

     return new State<TValue,TState>(s => selector(s).RunState(s)); 
    } 

    /// <summary>TODO</summary> 
    /// <typeparam name="TValue"></typeparam> 
    /// <typeparam name="TState"></typeparam> 
    /// <param name="selector"></param> 
    [Pure]public static State<Unit,TState> GetComposePut(
     Func<TState,TState> transform 
    ) { 
     transform.ContractedNotNull("transform"); 

     return new State<Unit,TState>(s => new State<Unit,TState>.StateTuple(Unit.unit,transform(s))); 
    } 

    /// <summary>TODO</summary> 
    /// <typeparam name="TValue"></typeparam> 
    /// <typeparam name="TState"></typeparam> 
    /// <param name="selector"></param> 
    [Pure]public static State<TValue,TState> GetSelect<TValue>(
     State<TValue,TState>.Transformer projector 
    ) where TValue:struct { 
     projector.ContractedNotNull("projector"); 

     return new State<TValue,TState>(projector); 
    } 
    #endregion 
    } 
+0

@Codexer Не думаю; У меня есть пример в моем коде, который все еще не компилируется, когда я заменяю лямбда статической функцией. Аналогично, когда я оставляю использование в лямбде как поле, ссылается на 's'. –

+0

Это все очень ошеломляет, но я мог бы предложить некоторое представление, если бы смог его собрать. Я не могу найти объявление StateTuple где угодно. Можно ли продолжать добавлять объявления до сборки? – recursive

+0

Где определение 'State .Get'? Недостаточно определённых определений для компиляции. – Lee

ответ

2

Я думаю, что ваше определение Select проблема. Он должен иметь тип State<T, S> -> Func<T, R> -> State<R, S>, т. Е. Он должен работать только с типом результата, а не с состоянием. Если добавить метод экземпляра с этой подписью:

public State<TResult, TState> Select<TResult>(Func<TValue, TResult> selector) 
{ 
    Transformer t = this._transformer; 
    var inner = new State<TResult, TState>.Transformer(s => 
    { 
     var inter = t(s); 
     return new State<TResult, TState>.StateTuple(selector(inter.Value), inter.State); 
    }); 
    return new State<TResult, TState>(inner); 
} 

Обратите внимание на приведенное выше определение требует struct ограничения (которые не должны быть необходимы) от TState и TResult параметров государственного типа.

, а затем зафиксировать тип ваших государственных выражений:

private static readonly State<TupleUnit, GCDState> GcdInner_Good = 
     (from s in State<Unit, GCDState>.Get 
     select new TupleUnit(Unit.unit, 
      s.A > s.B ? new GCDState(s.B, s.A - s.B) 
      : s.A < s.B ? new GCDState(s.B, s.A) 
         : s) 
    ); 

     private static readonly State<TupleUnit, GCDState> GcdInner_Bad = 
     (from s in State<Unit, GCDState>.Get 
     let A = s.A 
     let B = s.B 
     select new TupleUnit(Unit.unit, 
      A > B ? new GCDState(B, A - B) 
      : A < B ? new GCDState(B, A) 
         : s) 
    ); 

затем они оба типа проверки.

Выражение запроса, содержащее пункт, как let

from s in src 
let x = s.Expr 
select x 

будут преобразованы в:

src.Select(s => new { S = S, X = s.Expr }).Select(s => s.X); 

и компилятор не в состоянии определить тип промежуточного Select вызова. Вероятно, это потому, что это метод расширения и содержит параметр неиспользуемого типа. Если вы сделаете его методом экземпляра, вы можете устранить эту двусмысленность.

Следующая реализация State опор с использованием let внутри синтаксиса запросов:

public class State<TState, TResult> 
{ 
    private readonly Func<TState, StateResult<TState, TResult>> f; 

    public State(Func<TState, StateResult<TState, TResult>> f) 
    { 
     this.f = f; 
    } 

    public StateResult<TState, TResult> Run(TState state) 
    { 
     return this.f(state); 
    } 

    public TResult RunResult(TState state) 
    { 
     return this.f(state).Result; 
    } 

    public TState RunState(TState state) 
    { 
     return this.f(state).State; 
    } 

    public State<TState, TOut> Select<TOut>(Func<TResult, TOut> mapFunc) 
    { 
     Contract.Requires(mapFunc != null); 

     return new State<TState, TOut>(s => 
     { 
      var thisResult = this.f(s); 
      return new StateResult<TState, TOut>(s, mapFunc(thisResult.Result)); 
     }); 
    } 

    public State<TState, TOut> BiSelect<TOut>(Func<StateResult<TState, TResult>, StateResult<TState, TOut>> mapFunc) 
    { 
     return new State<TState, TOut>(s => 
     { 
      return mapFunc(this.f(s)); 
     }); 
    } 

    public State<TState, TOut> SelectMany<TOut>(Func<TResult, State<TState, TOut>> bindFunc) 
    { 
     return SelectMany(bindFunc, (_, r) => r); 
    } 

    public State<TState, TOut> SelectMany<TInter, TOut>(Func<TResult, State<TState, TInter>> bindFunc, Func<TResult, TInter, TOut> selector) 
    { 
     return new State<TState, TOut>(initialState => 
     { 
      var thisResult = this.f(initialState); 
      var nextState = bindFunc(thisResult.Result); 
      var nextResult = nextState.Run(thisResult.State); 
      var result = selector(thisResult.Result, nextResult.Result); 
      return new StateResult<TState, TOut>(nextResult.State, result); 
     }); 
    } 
} 

public static class State 
{ 
    public static State<TState, TResult> FromResult<TState, TResult>(TResult result) 
    { 
     return new State<TState, TResult>(s => new StateResult<TState, TResult>(s, result)); 
    } 

    public static State<TState, TState> Get<TState>() 
    { 
     return new State<TState, TState>(s => new StateResult<TState, TState>(s, s)); 
    } 

    public static State<TState, Unit> Put<TState>(TState state) 
    { 
     return new State<TState, Unit>(_ => new StateResult<TState, Unit>(state, Unit.Instance)); 
    } 

    public static State<TState, Unit> Modify<TState>(Func<TState, TState> modifyFunc) 
    { 
     return from s in Get<TState>() 
       from _ in Put(modifyFunc(s)) 
       select Unit.Instance; 
    } 
} 

public struct StateResult<TState, TResult> 
{ 
    public StateResult(TState state, TResult result) 
     : this() 
    { 
     this.State = state; 
     this.Result = result; 
    } 

    public TState State { get; private set; } 
    public TResult Result { get; private set; } 
} 
+0

Интересная линия мысли; но я не могу (хотя бы) определить, как реализовать семантику государственной монады для ** Выбрать ** с такой подписью типа. Первоначально у меня были все три метода расширения, настроенные аналогично этому, но мне пришлось перейти к тому, что мне теперь нужно, чтобы получить предложения * from * и * select * для компиляции и запуска LINQ; это, похоже, регрессия, но я буду преследовать ее немного дальше сегодня вечером. –

+1

@PieterGeerkens - я обновил ответ с помощью реализации для 'Select'. – Lee

+0

У вас есть некорректные детали, особенно, поскольку существующий 'Select' используется в другом месте для LINQ, но для этой подписи требуется вторая реализация:' public static State Выбрать (это State @this, Func проектор ) '. Однако это новое дает мне новую ошибку: «* Тип« Анонимный тип №1 »должен быть невообразимым типом значения, чтобы использовать его как параметр TResult в родовом типе или методе« ... Выберите (... Систему .Func ) '* ". –

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

  • Нет связанных вопросов^_^