2016-04-12 11 views
4

Sorry, wenn die Frage nicht zu klar ist. Ich bin mir nicht sicher, der beste Weg, um es zu formulieren (fühlen Sie sich frei zu bearbeiten!). Ich denke ein Beispiel wäre am klarsten:C++ - Konzepte: Kann ich ein Konzept definieren, das selbst eine Vorlage ist?

Ich versuche ein Monad-Konzept basierend auf der Haskell definition zu definieren. Der Bindungsoperator (>>=) erfordert, dass eine Monad vom Typ A an eine Funktion gebunden werden kann, die eine A übernimmt und eine Monad vom Typ B zurückgibt. Ich kann A in Bezug auf eine value_type Typedef definieren, aber wie definiere ich Typ B in meinem Konzept?

template <typename M> 
concept bool Monad() 
{ 
    return requires(M m, Function<_1, ValueType<M>> f) { 
    // (>>=) :: m a -> (a -> m b) -> m b 
    { m >>= f } -> M 
    } 
} 

Im obigen Beispiel, was stelle ich anstelle der _1 in der Funktion <> Konzept?

Begrenzt dies auch das Ergebnis, wenn f aufgerufen wird, um eine Monade eines beliebigen Typs zu sein?

+1

Nebenbei, (ab) mit 'operator >> =' hier scheint wie ein Fehler. In C++ assoziiert 'a >> = b >> = c' als 'a >> = (b >> = c)', während es in Haskell als '(a >> = b) >> = c' gilt. –

+0

ja ich weiß :) offensichtlich nicht produktions würdiger code - nur ein spiel zu lernen ein wenig über konzepte –

Antwort

1

Ich denke, das nächste, was Sie tun können, ist eine spezifische Funktion, die A --> Monad<B> ist und stellen Sie sicher, dass es das Richtige tut. In dem Bemühen, eine unendliche Rekursion zu verhindern, können wir nur bestätigen, dass A --> M Werke:

template <class M> 
concept bool Monad() 
{ 
    return requires(M m) { 
     { m >>= std::function<M(ValueType<M>)>{} } -> M; 
    }; 
} 

Dies ist nur ein Einzelfall, aber ich glaube nicht, ist es möglich, den allgemeinen Fall zu überprüfen, ob A --> Monad<X> Arbeiten, da die Concepts-Checks beinhalten immer noch spezifische Ausdrücke und Sie können nur bestimmte Ausdrücke mit bestimmten Typen erstellen.

Natürlich können wir mehrere solche Anforderungen bereitstellen. Mit einem rebind metafunction:

template <class M, class X> 
struct rebind; 

template <class M, class X> 
using rebind_t = typename rebind<M, X>::type; 

template <template <class...> class Z, class R, class X> 
struct rebind<Z<R>, X> { 
    using type = Z<X>; 
}; 

Wir können dann Anforderungen an Funktionen hinzufügen, verschiedene Arten der Rückkehr sagen, dass es funktioniert auch für int s:

template <class M> 
concept bool Monad() 
{ 
    return requires(M m) 
    { 
     { m >>= std::function<M(ValueType<M>)>{} } -> M; 
     { m >>= std::function<rebind_t<M,int>(ValueType<M>)>{} } -> rebind_t<M,int>; 
    }; 
} 

die leichter bekommen könnte damit in die eigenen Unter Refactoring -Konzept:

template <class M, class R> 
concept bool MonadBind() 
{ 
    return requires(M m) { 
     { m >>= std::function<rebind_t<M,R>(ValueType<M>)>{} } -> rebind_t<M,R>; 
    }; 
} 

template <class M> 
concept bool Monad() 
{ 
    return requires(M m) { 
     requires MonadBind<M, ValueType<M>>(); 
     requires MonadBind<M, int>(); 
    }; 
}