2015-10-14 2 views
6

Ho un tipo (chiamiamolo A) e voglio fare un typeclass delle funzioni di tipo A -> A, A -> A -> A, A -> A -> A -> ecc ... Questo non funziona:Haskell - Tutte le funzioni del modulo A -> A -> ... -> A

{-# LANGUAGE FlexibleInstances #-} 

data A = A 

class AsToA a where 
    takeA :: AsToA b => a -> A -> Either A b 

instance AsToA (A -> A) where 
    takeA f a = Left (f a) 

instance AsToA b => AsToA (A -> b) where 
    takeA f a = Right (f a) 

ottengo il seguente messaggio di errore:

AsToA.hs:12:22: 
    Couldn't match expected type ‘b1’ with actual type ‘b’ 
     ‘b’ is a rigid type variable bound by 
      the instance declaration at AsToA.hs:11:10 
     ‘b1’ is a rigid type variable bound by 
      the type signature for 

      takeA :: AsToA b1 => (A -> b) -> A -> Either A b1 
      at AsToA.hs:12:3 
    Relevant bindings include 
     f :: A -> b (bound at AsToA.hs:12:9) 
     takeA :: (A -> b) -> A -> Either A b1 (bound at AsToA.hs:12:3) 
    In the first argument of ‘Right’, namely ‘(f a)’ 
    In the expression: Right (f a) 

Tutte le idee? Grazie mille per qualsiasi consiglio.

+0

Sono sicuro al 90% che è perché 'takeA' è implicitamente esistenzialmente quantificata. –

risposta

2

Come accennato nei commenti alla risposta di altri, non si potrebbe bisogno realmente la Either, e takeA è quindi fondamentalmente sempre id, solo con una restrizione di tipo. Se è così si può fare questo un metodo di-meno classe:

{-# LANGUAGE FlexibleInstances, FlexibleContexts #-} 

data A = A 

class AsToA a 

takeA :: AsToA a => a -> a 
takeA = id 

instance AsToA (A -> A) 

instance AsToA (A -> b) => AsToA (A -> (A -> b)) 

In alternativa, si potrebbe desiderare di convertire le funzioni in un tipo comune che permette di passare in modo dinamico A s.Se è così Either non sarà sufficiente, ma è possibile definire il proprio:

{-# LANGUAGE FlexibleInstances, FlexibleContexts #-} 

data A = A 

data R = Result A | MoreArgs (A -> R) 

class AsToA a where 
    takeA :: a -> A -> R 

instance AsToA (A -> A) where 
    takeA f a = Result (f a) 

instance AsToA (A -> b) => AsToA (A -> (A -> b)) where 
    takeA f a = MoreArgs (takeA $ f a) 
+0

Questa seconda risposta è perfetta, grazie molte. Mi dispiace se non fossi stato molto chiaro prima. – RhubarbAndC

4

C'è una certa confusione tra i due b s:

class AsToA a where 
    takeA :: AsToA b => a -> A -> Either A b 

instance AsToA b => AsToA (A -> b) where 
    takeA f a = Right (f a) 

Questi non sono gli stessi. Diamo rinominare il primo a c

class AsToA a where 
    takeA :: AsToA c => a -> A -> Either A c 

instance AsToA b => AsToA (A -> b) where 
    takeA f a = Right (f a) 

Ora, Right (f a) ha digitare Either A b ma dovrebbe avere tipo Either A c per qualsiasi c tale che AsToA c stive. Questo non digita il controllo.

Il problema qui è che la firma

takeA :: AsToA c => a -> A -> Either A c 

promesse che takeA possa tornare Either A c per qualsiasic, la scelta del chiamante. Questo non è quello che vuoi, immagino.


Non sono ancora sicuro di quale sia il vero risultato previsto, ma suppongo che il problema sia simile al seguente.

Data una funzione f di tipo A->A->...->A restituire una funzione \x -> f x x ..., con una sola applicazione di x per ogni -> nel tipo (quindi di tipo A->A).

Una soluzione possibile è

{-# LANGUAGE FlexibleInstances, OverlappingInstances #-} 
data A = A -- could be anything 

class C f where 
    takeA :: f -> A -> A 

instance C (A -> A) where 
    takeA f = f 

instance C b => C (A -> b) where 
    takeA f = \x -> takeA (f x) x 

Si noti che questo richiede OverlappingInstances da utilizzare, che è abbastanza male . Consiglierei di evitarlo.

Per evitarlo, in questo caso è sufficiente definire un'istanza anche per il tipo A.

{-# LANGUAGE FlexibleInstances #-} 
data A = A -- could be anything 

class C f where 
    takeA :: f -> A -> A 

instance C A where 
    takeA a = \_ -> a 

instance C b => C (A -> b) where 
    takeA f = \x -> takeA (f x) x 
+1

* promette che takeA può restituire A o c per qualsiasi c * - quindi in sostanza, è il problema di 'takeA' essere dichiarato come' forall b. AsToA b = a -> a -> O A b', dove 'b' nell'istanza è rigida, giusto? –

+0

Quindi cosa mi suggerisci di fare? È possibile definire questo typeclass? – RhubarbAndC

+1

@RububarbAndC Non sono sicuro di cosa sia necessario '' O'' per –