2013-04-02 25 views
8

(Przepraszam za dziwny tytuł, ale nie mogłem wymyślić lepszego.)Jak ma operatora, który dodaje/odejmuje zarówno wartości bezwzględne i względne, w Haskell

Dla osobistego projektu Haskell I chcą mieć pojęcia "wartości bezwzględnych" (jak częstotliwość) i wartości względne (jak stosunek dwóch częstotliwości). W moim kontekście nie ma sensu dodawać dwóch wartości bezwzględnych: można dodawać względne wartości, aby wytworzyć nowe względne wartości, i dodać względną wartość do bezwzględnej, aby wytworzyć nową wartość bezwzględną (i podobnie do odejmowania).

Mam zdefiniowane klasy typów dla tych: patrz poniżej. Należy jednak zauważyć, że operatory ##+ i #+ mają podobną strukturę (i podobnie dla ##- i #-). Dlatego wolałbym scalić te operatory, tak, żebym miał jednego operatora dodawania, który dodaje względną wartość (i podobnie pojedynczy operator odejmowania, co powoduje względną wartość). AKTUALIZACJA: Aby wyjaśnić, moim celem jest ujednolicenie moich ##+ i #+ w jeden operator. Moim celem jest , a nie, aby ujednolicić to z istniejącym operatorem (Num) +.

Jednak nie widzę, jak to zrobić z klasami typów.

Pytanie: Czy można to zrobić, a jeśli tak, to w jaki sposób? A może nie powinienem próbować?

Oto co obecnie mam:

{-# LANGUAGE MultiParamTypeClasses #-} 

class Abs a where 
    nullPoint :: a 

class Rel r where 
    zero :: r 
    (##+) :: r -> r -> r 
    neg :: r -> r 

    (##-) :: Rel r => r -> r -> r 
    r ##- s = r ##+ neg s 

class (Abs a, Rel r) => AbsRel a r where 
    (#+) :: a -> r -> a 
    (#-) :: a -> a -> r 

Odpowiedz

9

myślę szukasz koncepcji nazywany Torsor. Torsor składa się z zestawu wartości, zestawu różnic i operatora, który dodaje różnicę do wartości. Dodatkowo zbiór różnic musi tworzyć grupę addytywną, więc różnice można również dodać razem.

Co ciekawe, torsory są wszędzie.Typowe przykłady obejmują

  • Punkty i wektory
  • Termin i data-różnice
  • plików i dyferencjału

itp

Jednym z możliwych definicji Haskell jest:

class Torsor a where 
    type TorsorOf a :: * 
    (.-) :: a -> a -> TorsorOf a 
    (.+) :: a -> TorsorOf a -> a 

Tutaj ar e kilka przykładowych:

instance Torsor UTCTime where 
    type TorsorOf UTCTime = NominalDiffTime 
    a .- b = diffUTCTime a b 
    a .+ b = addUTCTime b a 

instance Torsor Double where 
    type TorsorOf Double = Double 
    a .- b = a - b 
    a .+ b = a + b 

instance Torsor Int where 
    type TorsorOf Int = Int 
    a .- b = a - b 
    a .+ b = a + b 

W tym ostatnim przypadku należy zauważyć, że te dwa zestawy z torsors nie muszą być inny zestaw, który sprawia, dodawania wartości względne razem proste.

Aby uzyskać więcej informacji, zobacz a much nicer description in Roman Cheplyakas blog

+0

Myślę, że popełniłeś ten sam błąd co ja - pytanie nie dotyczy tego, jak zaimplementować klasę typu dla torsorów (lub przestrzeni afinicznych). Przeciwnie, chodzi o to, jak zunifikować operatory '. +' I '.-' W instancji 'Torsor' z bazowymi operatorami' + 'i' -'. –

+0

@ChrisTaylor Myślę, że najlepsze, co możesz zrobić, to ujednolicić '+' z '. +', A nie odwrotnie. Albo czekanie, że ktoś ugryzie kulę i odmieni klasę 'Num' w coś bardziej elastycznego. :) – aleator

8

Nie sądzę, należy starać się ujednolicić tych operatorów. Odejmowanie dwóch wektorów i odejmowanie dwóch punktów to zasadniczo różne operacje. Fakt, że trudno jest je przedstawić jako takie samo w systemie typu, nie jest systemem typu, który jest niezręczny - ponieważ te dwie koncepcje są naprawdę różne!

Ramy matematyczne, nad którymi pracujesz, to affine space.

Są one już dostępne w pakiecie Haskell w pakiecie przestrzeni wektorowej (do cabal install vector-space w wierszu polecenia). Zamiast używać klas typu wieloparametrowego, używają rodzin typów, aby powiązać typ wektorowy (względny) z każdym typem punktu (bezwzględnym).

Oto minimalne przykład pokazujący jak zdefiniować własne bezwzględnych i względnych typy danych, a ich interakcja:

{-# LANGUAGE TypeFamilies #-} 

import Data.VectorSpace 
import Data.AffineSpace 

data Point = Point { px :: Float, py :: Float } 

data Vec = Vec { vx :: Float, vy :: Float } 

instance AdditiveGroup Vec where 
    zeroV = Vec 0 0 
    negateV (Vec x y) = Vec (-x) (-y) 
    Vec x y ^+^ Vec x' y' = Vec (x+x') (y+y') 

instance AffineSpace Point where 
    type Diff Point = Vec 
    Point x y .-. Point x' y' = Vec (x-x') (y-y') 
    Point x y .+^ Vec x' y' = Point (x+x') (y+y') 
8

Masz dwie odpowiedź z informacją, co należy zrobić, oto kolejna odpowiedź z informacją, jak robić to, co prosiłeś (co może nie być dobrym pomysłem). :)

class Add a b c | a b -> c where 
    (#+) :: a -> b -> c 

instance Add AbsTime RelTime AbsTime where 
    (#+) = ... 
instance Add RelTime RelTime RelTime where 
    (#+) = ... 

przeciążenia dla (#+) sprawia, że ​​bardzo elastyczny. Zbyt elastyczne, IMO. Jedynym ograniczeniem jest to, że typ wyniku jest określany przez typy argumentów (bez tego FD operator staje się prawie bezużyteczny, ponieważ nie ogranicza niczego).

Powiązane problemy