Различные типы сеттеров и геттеров в линзах Haskell

У меня есть data typeG, которые получили поле _repr :: Data.Graph.Inductive.Gr String String, Обычный способ, при добавлении нового узла в Gr график, мы должны предоставить LNode a объект, который в основном определяется как кортеж (Int, a)где Int - индекс узлов в Graph - см. пример функции add ниже.

Я хочу реализовать функцию addx, который будет автоматически вычислять индекс (например, используя Data.Graph.Inductive.newNodes функция). Я хочу addx иметь подпись addx :: String -> G -> Int и эта функция будет вычислять новый свободный индекс, изменять график G и возвращать этот вычисленный индекс. Возможно ли в Haskell создать такую ​​функцию (которая изменит существующий объект - G в данном случае) - используя линзы или что-то в этом роде?

Я видел, что объектив Хаскелла определяется как lens :: (a -> c) -> (a -> d -> b) -> Lens a b c d а линза - это в основном "геттер" и "сеттер", поэтому ее сигнатура допускает различные типы выходных данных геттера (c), установочное значение (d) и установочный выход (b).

import qualified Data.Graph.Inductive     as DG

data G = G { _repr :: DG.Gr String String, _name::String} deriving ( Show )

empty :: G
empty = G DG.empty ""

add :: DG.LNode String -> G -> G
add node g = g{_repr = DG.insNode node $ _repr g}

-- is it possible to define it?
addx :: String -> G -> Int
addx name g = undefined

main :: IO ()
main = do
    let g = add (1, "test2")
          $ add (0, "test1")
          $ empty

        n1 = addx "test2" g
        g2 = DG.insEdge(n1,0)
           $ DG.insEdge(0,1)

    print $ g

1 ответ

Ваш тип для addx сломан, так как вы не можете изменить G в чистой функции без возврата измененной формы, такой как addx1 :: String -> G -> (Int, G), Если у вас есть умный взгляд на монады Хаскелла, вы можете заметить, что он имеет изоморфный тип, addx2 :: String -> State G Int,

Мы можем все выровнять к этой "государственной" ориентации

add' node = do g <- get
               put $ g { _repr = DB.insNode node $ _repr g }

и сделать его более кратким с линзами

add'' node = repr %= DB.insNode node

Настоящая проблема здесь заключается в том, чтобы, в конце концов, отследить идентичность узла. Одним из способов было бы нести его вместе с repr в вашем типе

data G = G { _repr :: DG.Gr String String, _name :: String, _index :: Int } 
empty = G DG.empty "" 0

затем используйте это при построении узлов (снова используя линзы!)

addx' name = do i <- use index
                repr %= DB.insNode (i, node)
                i += 1
Другие вопросы по тегам