2013-04-06 55 views
5

我工作的HList實施異構數據結構,我堅持努力實現map功能吧。我已經嘗試了很多不同的方法,但是每次都遇到與該函數相關的編譯器錯誤。映射了一個通用的功能

以下是我要如何使用通用功能Just,將其應用到輸入數據結構中的所有元素的例子。

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances #-} 

-- | An input heterogenous data structure 
recursivePairs :: (Int, (Char, (Bool,()))) 
recursivePairs = (1, ('a', (True,()))) 

-- | This is how I want to use it 
recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool,()))) 
recursivePairs' = hMap Just recursivePairs 

class HMap f input output where 
    hMap :: f -> input -> output 

-- | A counterpart of a Nil pattern match for a list 
instance HMap f()() where 
    hMap _ _ =() 

-- | A counterpart of a Cons pattern match for a list 
instance 
    (HMap f iTail oTail, 
    Apply f iHead oHead) => 
    HMap f (iHead, iTail) (oHead, oTail) 
    where 
    hMap f (head, tail) = (apply f head, hMap f tail) 

class Apply f input output where 
    apply :: f -> input -> output 

instance Apply (input -> output) input output where 
    apply = id 

有了這個,我發現了以下編譯器錯誤:

No instance for (Apply (a0 -> Maybe a0) Int (Maybe Int)) 
    arising from a use of `hMap' 
The type variable `a0' is ambiguous 

有沒有在所有的方式來解決這一點,如果沒有的話,爲什麼?

+0

我認爲這個問題是該類型系統不會意識到你正在實例化'Just'不同的具體類型上的每個連續的申請,因爲您'hMap'的定義不斷重複使用相同的'F'。第一次應用它時,類型是'Int - > Maybe Int',第二次應用它時,類型是'Char - > Maybe Char'。不過,我仍然不確定如何解決這個問題。 – 2013-04-06 23:00:40

+0

@GabrielGonzalez 是的,這也正是問題。如果你添加一個fundep'|輸入輸出 - > F'到'Apply'類,錯誤消息會說,它正在尋找的情況下,像'(BOOL - >也許布爾)字符(字符也許)'。我正在考慮使用['cast'](http://hackage.haskell。org/packages/archive/base/latest/doc/html/Data-Typeable.html#v:cast)在類型級別斷開'f'的兩個用法,但這並不是很自然,取決於「Typeable」也不是很誘人。 – 2013-04-07 18:48:38

回答

5

的問題是,你要使用不同的參數多態的功能,但你Apply實例需要的功能(單式)。您可以輕鬆地解決這個多種方式

data JustIfy = JustIfy 
instance Apply JustIfy a (Maybe a) where 
    apply _ = Just 

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool,()))) 
recursivePairs' = hMap JustIfy recursivePairs 

作品與您的代碼就好了

編輯:一個更通用的方法來同樣的事情(需要RankNTypes

--A "universal" action that works on all types 
newtype Univ f = Univ (forall x. x -> f x) 
instance Apply (Univ f) x (f x) where 
    apply (Univ f) x = f x 

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool,()))) 
recursivePairs' = hMap (Univ Just) recursivePairs 

,或者如果你是使用最近的ish版本的GHC並願意打開更多的擴展

newtype Univ' c f = Univ' (forall x. c x => x -> f x) 
instance c x => Apply (Univ' c f) x (f x) where 
    apply (Univ' f) x = f x 

class All x 
instance All x 

recursivePairs' :: (Maybe Int, (Maybe Char, (Maybe Bool,()))) 
recursivePairs' = hMap (Univ' Just :: Univ' All Maybe) recursivePairs 

這是很好的,因爲它可以讓你做一些事情,比如在你映射的函數中包含一個「show」。

對於一個更通用的解決方案,請Oleg's Type level lambda caclulus它允許你在價值層面編寫代碼,然後自動神奇地推斷適當的類型級程序。不幸的是,奧列格的解決方案在這一點上比較陳舊,並且使用了我不太喜歡的LC的名義實現。我一直在考慮如何做得更好,但可能會延續下去,直到可靠的平等來打造家庭。

我的看法是,應該HLists這些天使用GADTs和DataKinds而不是元組來完成。類型族優於函數依賴,但由於缺乏可判斷的平等,因此目前受到更多限制。

+0

謝謝。你說有多種方法可以解決這個問題 - 請你詳細說明一下嗎?我正在尋找一個最佳的解決方案,因此沒有任何要求堅持提供的代碼,它只是一個抽象的例子。有沒有辦法解決這個問題,而不必爲每個想用'hMap'的函數聲明特定的實例呢? – 2013-04-07 18:42:27

+0

@NikitaVolkov我添加了更多通用解決方案 – 2013-04-07 23:17:59

1

雖然下面的不完全應答(所以我不會接受它)的問題,它確實解決好,而無需任何額外的情況下爲應用性仿函數映射結構問題:

{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE FlexibleInstances #-} 

import Control.Applicative 

main = do 
    print $ (hPure recursivePairs :: (Maybe Int, (Maybe Char, (Maybe Bool,())))) 
    print $ (hPure recursivePairs :: ([Int], ([Char], ([Bool],())))) 

recursivePairs :: (Int, (Char, (Bool,()))) 
recursivePairs = (1, ('a', (True,()))) 

class HPure input output where 
    hPure :: input -> output 

instance HPure()() where 
    hPure _ =() 

instance 
    (Applicative f, 
    HPure iTail oTail) => 
    HPure (iHead, iTail) (f iHead, oTail) 
    where hPure (iHead, iTail) = (pure iHead, hPure iTail) 

輸出:

(Just 1,(Just 'a',(Just True,()))) 
([1],("a",([True],())))