2013-10-23 60 views
4

所以最初我寫道:如何通過Haskell中另一個函數的參數映射函數?

xs <- getAddrInfo (Just hints) (Just addr) (Just port) 

然後在我看來,該功能「只是一個:: - >也許」是「映射」在「提示」,「地址」和「端口類型的」,所以我想出了這樣的事情:

map_arg g f a b c = f (g a) (g b) (g c) 
xs <- map_arg Just getAddrInfo hints addr port 

但GHC預計(GA),(GB)和(GC)是同一類型的,所以這並不類型檢查。

有沒有辦法做到這一點,或更一般地說,有沒有辦法將函數映射到另一個函數的參數上?

回答

5

一個最普通類型的簽名會看起來像

map_arg :: (forall b.b -> a b) -> (a b -> a c -> a d -> e) -> b -> c -> d -> e 
map_arg g f a b c = f (g a) (g b) (g c) 

對於你的情況,如果你選擇不把g作爲參數,你可以做

map_just f a b c = f (g a) (g b) (g c) where g = Just 
xs <- map_just getAddrInfo hints addr port 

或者你可以給類型僅限於g的簽名:

map_arg (g :: forall b.b -> a b) f a b c = f (g a) (g b) (g c) 

要繞過p olymorphic類型簽名,還記得我們有Control.Functor.Pointed所以你可以使用它:

map_arg :: Pointed p => (p a -> p b -> p c -> d) -> a -> b -> c -> d 
map_arg f a b c = f (point a) (point b) (point c) 

(的PointedMaybe的實現是Just你想要的)

爲了有一個通用版本,注意

map1 :: Pointed p => (p a -> b) -> a -> b 
map1 f = f . point 

map2 :: Pointed p => (p a -> p b -> c) -> a -> b -> c 
map2 f = map1 . map1 f 

map3 :: Pointed p => (p a -> p b -> p c -> d) -> a -> b -> c -> d 
map3 f = map2 . map1 f 

看到了嗎?你只需要map1和其他所有的只是簡單的組合!

+1

所以沒有類型簽名,GHC只是假設'g'必須是單形的? (爲什麼?只是一個簡化的假設?)...'g'的類型簽名聲明'g'在其參數中是多態的,並返回一個結果,其類型取決於參數的類型。如果你希望返回類型不是'ab'形式(例如,'g = id',或'g = fromIntegral'和一個類型約束),我想你需要設置一個類型族或使用'g'的fundep。 (?) – misterbee

+2

@misterbee我認爲這是Hindley-Milner類型系統的限制,它是基於GHC的。如果沒有多態類型簽名,Haskell無法進行多態類型推斷,除非該變量使用'where'或'let'子句進行綁定。 –

+0

@misterbee我想你是正確的使用類型家庭或fundep,雖然我不知道如何做(尚)。 –

4

簡短的回答是否定的(如果您正在討論適用於任意數量參數的通用map_arg)。

你或許可以用Oleg-level類型的魔術來實現這一點,但如果你只是想找到改進代碼的方法,那麼在這裏沒有多少改進。

2

只需將一個類型註釋添加到map_arg並使g多態。

map_arg :: (forall a. a -> Maybe a) -> 
      (Maybe AddrInfo -> Maybe HostName -> Maybe ServiceName -> IO [AddrInfo]) -> 
      AddrInfo -> HostName -> ServiceName -> IO [AddrInfo] 
map_arg g f a b c = f (g a) (g b) (g c) 

你需要Rank2TypesRankNTypes擴展做到這一點。

+2

請注意,此解決方案僅適用於'getAddrInfo'函數或具有相同簽名的任何其他函數,因此它不是所要求問題的「通用」解決方案。 – Ankur

相關問題