packages feed

proton-0.0.1: src/Examples/Flowers.hs

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Examples.Flowers where

import Proton

import Data.Profunctor.Rep
import Data.Profunctor.Strong
import Data.Profunctor.Sieve
import Data.Foldable

import qualified Data.Map as M
import Data.List
import Data.Ord
import Data.Function
import Control.Applicative
import Debug.Trace
import Data.Functor.Identity
import Proton.Algebraic
import Data.Profunctor

data Species = Setosa | Versicolor | Virginica
  deriving Show

data Measurements = Measurements {getMeasurements :: [Float]}
  deriving Show

data Flower = Flower {flowerSpecies :: Species, flowerMeasurements :: Measurements}
  deriving Show

-- measurements' :: p [Float] [Float] -> p Measurements Measurements
-- measurements' :: Lens' Measurements [Float]
-- measurements' p =

measurementDistance :: Measurements -> Measurements -> Float
measurementDistance (Measurements xs) (Measurements ys) =
    sqrt . sum $ zipWith diff xs ys
  where
    diff a b = (a - b) ** 2

-- aggregate :: Kaleidoscope' Measurements Float
aggregate :: Kaleidoscope' Measurements Float
aggregate = iso getMeasurements Measurements . pointWise

-- measureLens :: Lens' Measurements [Float]
-- measureLens = lens getMeasurements setter
--   where
--     setter _ b = Measurements b

classify :: [Flower] -> Measurements -> Maybe Flower
classify flowers m
  | null flowers = Nothing
  | otherwise =
  let Flower species _ = minimumBy
                          (comparing (measurementDistance m . flowerMeasurements))
                          flowers
   in Just $ Flower species m

-- measurements :: (Corepresentable p, Foldable (Corep p)) => Optic' p Flower Measurements
-- measurements = listLens flowerMeasurements classify

measurements :: AlgebraicLens Flower (Maybe Flower) Measurements Measurements
measurements = listLens flowerMeasurements classify

-- strained :: forall s b. ListLens s [s] s Bool
-- strained = listLens id go
--   where
--     -- go :: ([s], [Bool]) -> [s]
--     -- go  = fmap fst . filter snd . uncurry zip
--     go  (x, True)  = x
--     go  (x, False) = []

versicolor :: Flower
versicolor = Flower Versicolor (Measurements [2, 3, 4, 2])

setosa :: Flower
setosa = Flower Setosa (Measurements [5, 4, 3, 2.5])

flowers :: [Flower]
flowers = [versicolor, setosa]

mean :: Fractional a => [a] -> a
mean [] =  0
mean xs = sum xs / fromIntegral (length xs)


infixr 4 >--
(>--) :: [s] -> Optic (Costar []) s t a a -> t
(>--) xs opt = (runCostar $ opt (Costar head)) xs

aggregateWith :: Functor f => (f Float -> Float) -> Optic (Costar []) Measurements Measurements Float Float
aggregateWith aggregator p = Costar (Measurements . fmap (cosieve p) . transpose . fmap getMeasurements)

avgMeasurement :: Foldable f => f Measurements -> Measurements
avgMeasurement ms = Measurements (mean <$> groupedMeasurements)
  where
    groupedMeasurements :: [[Float]]
    groupedMeasurements = transpose (getMeasurements <$> toList ms)
    mean :: [Float] -> Float
    mean xs = sum xs / fromIntegral (length xs)

applyWeight :: Float -> Measurements -> Measurements
applyWeight w (Measurements m) = Measurements (fmap (*w) m)

partitioned :: forall f a. (Ord a) => AlgebraicLens a ([a], [a]) a a
partitioned = listLens id splitter
  where
    -- splitter :: f a -> a -> ([a], [a])
    splitter xs ref
      = (filter (< ref) (toList xs), filter (>= ref) (toList xs))

onFirst :: Eq a => AlgebraicLens (a, b) (Maybe b) a a
onFirst = listLens fst picker
  where
    picker xs a = lookup a $ toList xs

selectingOn :: (s -> a) -> AlgebraicLens s (Maybe s) a (Maybe Int)
selectingOn project = listLens project picker
  where
    picker xs i = (toList xs !!) <$> i

indexOf :: Eq s => AlgebraicLens s (Maybe Int) s s
indexOf = listLens id (flip elemIndex . toList)

test :: IO ()
test = do
    -- print $ [1..10] & partitioned ?- (5 :: Int)
    -- print $ [1..10] & partitioned >- mean
    -- print $ ["banana", "pomegranate", "watermelon"] & selectingOn length >- elemIndex 11
    -- print $ ["banana", "pomegranate", "watermelon"] & selectingOn length . indexOf ?- 11
    -- print $ Identity "banana" & selectingOn length . indexOf %~ (+10)
    -- print $ (flowers >-- (measurements . aggregateWith mean))
    print $ flowers & (measurements . aggregate >- mean)
    -- We can use a list-lens as a setter over a single element
    -- print $ versicolor & measurements . aggregate %~ negate

    -- -- We can explicitly compare to a specific result
    -- print $ (flowers !! 1) ^. measurements
    -- print $ (flowers ?. measurements) $ Measurements [5, 4, 3, 1]
    -- print $ Measurements [5, 4, 3, 1] & (measurements .* flowers)
    -- print $ Measurements [5, 4, 3, 1] & measurements .* flowers

    -- -- We can provide an aggregator explicitly
    -- print $ mean & (flowers >- measurements . aggregate)
    -- print $ flowers & measurements >- avgMeasurement
    -- print $ M.fromList [(1.2, setosa), (0.6, versicolor)] & measurements >- avgMeasurement . fmap (uncurry applyWeight) . M.toList
    -- print $ flowers & (measurements . aggregate *% mean)
    -- print $ flowers & (measurements . aggregate *% mean)
    -- print $ flowers & (measurements . aggregate *% maximum)
    -- print $ [[1, 2, 3], [1, 2, 3], [1, 2, 3]] & convolving *% id
    --


allMeasurements :: [[Float]]
allMeasurements =
      [ [1  , 2  , 3  , 4  ]
      , [10 , 20 , 30 , 40 ]
      , [100, 200, 300, 400]
      ]

measurementMap :: M.Map String (ZipList Float)
measurementMap = M.fromList
      [ ("setosa"    , ZipList [1  , 2  , 3  , 4  ])
      , ("versicolor", ZipList [10 , 20 , 30 , 40 ])
      , ("virginica" , ZipList [100, 200, 300, 400])
      ]