Как «перегрузить» долларового оператора в Haskell - PullRequest
0 голосов
/ 09 октября 2018

Прежде всего, извините, если этот вопрос уже задавался, я просто не мог найти правильные английские термины, чтобы выразить то, что я имел в виду.

Мне было интересно, был ли какой-нибудь класс типов в Haskell, представляющий функциюприложение, чтобы определить несколько поведения для разных типов данных.

Используя пакет Graphics.X11.Xlib, я натолкнулся на множество различных функций, запрашивающих одинаковые параметры.Таким образом, моя идея заключалась в том, чтобы упаковать эти функции в кортеж (поскольку их тип возврата не совпадает) и передать им все параметры сразу.Примерно так:

import Graphics.X11.Xlib

main = do
  display <- openDisplay ":0"
  let dScreen = defaultScreen display
      (black, white, cMap) =
      -- here is where the "parameter dispatch" is needed
      (blackPixel, whitePixel, defaultColormap)  display dScreen

  -- computation
  return ()

Я ничего не нашел, поэтому я решил создать этот тип класса:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
import Graphics.X11.Xlib

class Dispatch f x y | x y -> f where
  dsp :: f -> x -> y
instance Dispatch (a -> b, a -> c, a -> d) a (b, c, d) where
  dsp (f, g, h) x = (f x, g x, h x)

main = do
  display <- openDisplay ":0"
  let dScreen = defaultScreen display
      (black, white, cMap) =
      -- here is where the "parameter dispatch" is needed
      (blackPixel, whitePixel, defaultColormap) `dsp` display `dsp` dScreen

  -- computation
  return ()

Он отлично работает и умножает экземпляры для разных кортежей.размеров, можно просто добавлять или удалять функции из «кортежа функций» в зависимости от необходимых значений, и код все равно будет компилироваться.

Но есть ли способ сделать это без этого обходного пути?Я пытался использовать Control.Applicative или Control.Arrow, но функции с несколькими параметрами плохо заканчиваются.

Моя лучшая попытка на данный момент: (,) <$> blackPixel <*> whitePixel

Ответы [ 2 ]

0 голосов
/ 10 октября 2018

Вы можете применить много функций к одному аргументу с семейством функций liftA.Вложите их, чтобы применить много функций ко многим аргументам.Итак:

(b, w, m) = liftA3 (liftA3 (,,)) blackPixel whitePixel defaultColormap display dScreen
0 голосов
/ 09 октября 2018

Не совсем ответ, но вот как вы могли бы расширить эту идею до истинной «перегрузки оператора $»:

{-# LANGUAGE TypeFamilies, FlexibleInstances #-}

import Prelude hiding (($))

infixr 0 $

class Dispatch f where
  type Argument f :: *
  type Result f :: *
  ($) :: f -> Argument f -> Result f

instance Dispatch (a -> b) where
  type Argument (a->b) = a
  type Result (a->b) = b
  f $ x = f x

instance (Dispatch x, Dispatch y, Argument x ~ Argument y)
            => Dispatch (x,y) where
  type Argument (x,y) = Argument x
  type Result (x,y) = (Result x, Result y)
  (f,g) $ a = (f $ a, g $ a)

instance ( Dispatch x, Dispatch y, Dispatch z
         , Argument x ~ Argument y, Argument y ~ Argument z )
            => Dispatch (x,y,z) where
  type Argument (x,y,z) = Argument x
  type Result (x,y,z) = (Result x, Result y, Result z)
  (f,g,h) $ a = (f $ a, g $ a, h $ a)

main :: IO ()
main = do
   print $ ((\x -> ((2*x+),(3*x+)), (**), logBase) $ 2) $ 4
((8.0,10.0),16.0,2.0)
...