module Proton.Feedback where

import Data.Profunctor
import Proton.Types
import Proton.Setter
import Data.Function
import Debug.Trace

type Feedback s t a b = forall p. Costrong p => p a b -> p s t
type Feedback' s a = Feedback s s a a

-- Simplified:
-- feedback :: forall p s t a b state. Costrong p
--      => ((a, b) -> b) -> (b -> (a, b)) -> Optic' p a b
feedback :: forall p s t a b. Costrong p
     => ((s, b) -> a) -> (b -> (t, b)) -> Optic p s t a b
feedback :: ((s, b) -> a) -> (b -> (t, b)) -> Optic p s t a b
feedback ping :: (s, b) -> a
ping pong :: b -> (t, b)
pong = p (s, b) (t, b) -> p s t
forall (p :: * -> * -> *) a d b.
Costrong p =>
p (a, d) (b, d) -> p a b
unfirst (p (s, b) (t, b) -> p s t)
-> (p a b -> p (s, b) (t, b)) -> Optic p s t a b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((s, b) -> a) -> (b -> (t, b)) -> p a b -> p (s, b) (t, b)
forall (p :: * -> * -> *) a b c d.
Profunctor p =>
(a -> b) -> (c -> d) -> p b c -> p a d
dimap (s, b) -> a
ping b -> (t, b)
pong

-- factorial :: Feedback' Int Int
-- factorial = feedback ping pong
--   where
--     ping :: (Int, Int) -> Int
--     ping ~(new, acc) = new * acc
--     pong :: Int -> (Int, Int)
--     pong n = (n - 1, 3)

fib :: Feedback Int [Int] [Int] [Int]
fib :: p [Int] [Int] -> p Int [Int]
fib = ((Int, [Int]) -> [Int])
-> ([Int] -> ([Int], [Int])) -> p [Int] [Int] -> p Int [Int]
forall (p :: * -> * -> *) s t a b.
Costrong p =>
((s, b) -> a) -> (b -> (t, b)) -> Optic p s t a b
feedback (Int, [Int]) -> [Int]
ping [Int] -> ([Int], [Int])
pong
  where
    ping :: ((Int, [Int]) -> [Int])
    ping :: (Int, [Int]) -> [Int]
ping (n :: Int
n, xs :: [Int]
xs) = Int -> [Int] -> [Int]
forall a. Int -> [a] -> [a]
take Int
n [Int]
xs
    pong :: [Int] -> ([Int], [Int])
    pong :: [Int] -> ([Int], [Int])
pong xs :: [Int]
xs = ([Int] -> [Int]
forall a. [a] -> [a]
reverse [Int]
xs, [Int]
xs)

-- >>> 10 & fib %~ \xs -> 1 : 1 : zipWith (+) xs (tail xs)
-- [1,1,2,3,5,8,13,21,34,55,89]