Aby spróbować uprościć ten problem mam z definicją tych funkcji strzałka:Czy są jakieś użyteczne abstrakcje dla składni rekordu Haskell?
splitA :: (Arrow arr) => arr a b -> arr a (b,a)
splitA ar = ar &&& (arr (\a -> id a))
recordArrow
:: (Arrow arr)
=> (d -> r)
-> (d -> r -> d)
-> (r -> r)
-> arr d d
recordArrow g s f = splitA (arr g >>^ f) >>^ \(r,d) -> s d r
Które następnie niech mi zrobić coś takiego:
unarrow :: ((->) b c) -> (b -> c) -- unneeded as pointed out to me in the comments
unarrow g = g
data Testdata = Testdata { record1::Int,record2::Int,record3::Int }
testRecord = unarrow $
recordArrow record1 (\d r -> d { record1 = r }) id
>>> recordArrow record2 (\d r -> d { record2 = r }) id
>>> recordArrow record3 (\d r -> d { record3 = r }) id
Jak widać nie ma to bardzo dobre wykorzystanie DRY.
Mam nadzieję, że może istnieć rozszerzenie języka, które może uprościć ten proces. Tak że powyższe może po prostu być ponownie zapisać jako:
testRecord' = unarrow $
recordArrow' record1 id
>>> recordArrow' record2 id
>>> recordArrow' record3 id
Aktualizacja dla jasności: Aby wyjaśnić trochę. Jestem świadomy, że mógłbym zrobić coś takiego:
foo d = d { record1 = id (record1 d), record2 = id (record2 d) }
Ale to ignoruje wszelkie zlecenia wykonania i dowolny stan. Załóżmy, że funkcja aktualizacji dla record2
opiera się na zaktualizowanej wartości dla record1
. Lub alternatywnie, mogę chcieć utworzyć inną strzałkę, która wygląda tak: arr d (d,x)
, a następnie chcę zbudować listę [x]
, której kolejność zależy od kolejności oceny zapisów.
Co znalazłem to, że często chcę wykonać niektóre funkcje, a następnie zaktualizować rekord. Można to zrobić przez gwintowania stan jak ten
g :: d -> r -> d
foo d = let d' = d { record1 = (g d) (record1 d) } in d' { record2 = (g d') (record2 d') }
Ale myślę, że strzałka jest notacja neater i mogłem mieć [arr d d]
i łańcucha je razem w sekwencji. Plus jeśli r
lub są Monadami, to tworzy to lepszy kod. Albo jeśli obie są Monadami, to pozwalam mi wykonywać warstwowe wiązania bez konieczności używania Monad Transformatora. W przypadku ST s x
, pozwól mi łączyć w uporządkowany sposób stan s
.
Nie próbuję rozwiązać jednego konkretnego problemu. Po prostu próbuję znaleźć abstrakcyjną metodę aktualizowania składni rekordów bez konieczności jednoznacznego definiowania jakiegoś "gettera" i "setera".
Poniżej odpowiedział w comments-- Sidenote: miałem do definiowania funkcji unarrow
do konwersji funkcję (->) strzałkę z powrotem do funkcji. W przeciwnym razie, jeśli mam someArrow b
dla strzałki arr b c
, nie mogę uzyskać wartości c
. Dzięki funkcji unarrow
mogę napisać unarrow someArrow b
i działa dobrze. Czuję, że muszę zrobić coś złego tutaj, ponieważ moja definicja dla unarrow jest po prostu unarrow g = g
.
Funkcja 'unarrow' może zostać usunięta, jeśli podasz' testRecord' jawny typ podpisu. Wystąpił problem z wnioskiem typu, nie można stwierdzić, że 'testRecord' powinien być typem funkcji. – bmaderbacher
Byłoby miło, gdybyś mógł trochę wyjaśnić, czego nie możesz osiągnąć. Czy chcesz zastosować funkcje (id w tym przypadku) do pól rekordu, jak zakładałem dla mojej odpowiedzi? – bmaderbacher
Dzięki za wskazówkę na temat 'unarrow' @bmaderbacher. Rozgryzałem się w głowie, dlaczego nie mogłem zastosować strzałki w niektórych przypadkach - jednoznacznie definiując, że jest to funkcja w sygnaturze typu, wydaje się teraz oczywista. – TheCriticalImperitive