[Agda] stdlib for practical programming
Sergei Meshveliani
mechvel at botik.ru
Fri Sep 27 14:51:04 CEST 2013
On Thu, 2013-09-26 at 22:44 +0300, Dmytro Starosud wrote:
> OK, I see I should have been more precise.
> Yes, Standard library is "some library" but it doesn't allow enough
> level of overloading.
> What I need? A lot of stuff.
> In particular I need map (fmap, _<$>_ whatever) function which will
> work for any functor (Maybe, List, Vector, Product, mapable; IMapable
> whatever).
In Haskell this is achieved by the
language feature of constructor classes.
For example, Functor is a constructor class defined in the Haskell's
library:
class Functor c where fmap :: (a → b) → c a → c b
-- fmap f (c a) --> c (f a)
-- if I recall correctly!
Here c ranges over unary type constructors.
For example, c may be List, Maybe, ...
I think that Agda does not need any additional language feature for
this. This notion of Functor
(let us call it FMap, because I am not sure that Haskell's `Functor'
means something close to Functor of the category theory)
is easily expressed via dependent types and records:
record FMap (Cons : Set → Set) : Set (suc 0ℓ)
where
field fmap : {A B : Set} → (f : A → B) → Cons A → Cons B
(I skip the Level arguments, so far).
Here Cons correponds to `c' in Functor of the Haskell library.
This c FMap an be set into Standard library.
But I suspect that Standard library already has something for this,
may be something better.
Who knows, please, has it?
Anyway here is an example of using overloading for fmap via the above
FMap `class':
---------------------------------------------------------------------
module FMap where
open import Level using (suc) renaming (zero to 0ℓ)
open import Data.Bool using (Bool; true; false)
open import Data.Nat using (ℕ)
open import Data.Product using (_×_; _,_)
open import Data.List using (List; []; _∷_)
open import Data.Maybe using (Maybe; just; nothing)
-- First, define various instances for FMap.
-- Such instances can be easily defined and set into the library:
listFMap : FMap List -- here Cons == List
listFMap = record{ fmap = fmap-l }
where
fmap-l : {A B : Set} → (f : A → B) → List A → List B
fmap-l _ [] = []
fmap-l f (x ∷ xs) = (f x) ∷ (fmap-l f xs)
maybeFMap : FMap Maybe -- here Cons == Maybe
maybeFMap = record{ fmap = fmap-mb }
where
fmap-mb : {A B : Set} → (f : A → B) → Maybe A → Maybe B
fmap-mb f nothing = nothing
fmap-mb f (just a) = just (f a)
---------------------------------------------------------------------------
Example of usage:
g : Maybe Bool → Maybe ℕ → Maybe ℕ × Maybe Bool
g mbBoo mbN = (fmap f mbBoo , fmap h mbN)
where
open FMap maybeFMap
f : Bool → ℕ
f true = 1
f false = 0
h : ℕ → Bool
h 1 = true
h _ = false
We see that fmap is used as overloaded.
> I don't want import renaming ten modules just to do some simple things.
> I was asking for library, which simplifies all this work.
> e.g. I would like following:
>
> open import Prelude -- Prelude is this wanted "some library"
> {- many useful stuff here -}
>
> instead of:
>
> open import Level renaming (zero to zeroL; suc to sucL; \lub to \lubL)
> open import Data.Nat renaming (zero to zeroN; suc to sucN; \lub to \lubN; ...)
> open import Data.List renaming (map to mapL; foldr to foldrL; ...)
> open import Data.Vector renaming (map to mapV; foldr to foldrV; ...)
> open import Data.Product renaming (map to mapP; ...)
> etc. ...
> etc. ...
> etc. ...
>
> {- many useful stuff here -}
>
This `sugar' can (probably) be done by
importing `classes' (done as records) and instances
instead of separate functions.
If Haskell and its library is considered as all right in this import
matters, what is wrong with the Agda library in this point?
For example, instead of zero and suc, one can import
DistributiveLattice and its several instances
(NatProp.distributiveLattice, Foo.distributiveLattice, ...)
(a contrived example)
and then to use overloading by open DistributiveLattice {{...}}
and such.
This will give importing items by _groups_.
For example, 0#, 1# _+_; _*_ do not need to be renamed, because one
can import the `classs' RingWithOne and several its instances.
Introducing more `classes' and instances will support this style
further.
If I needed a single module Prelude
which exports "everything I may needed", I could try to write
module MyPrelude where
open import Foo1 using (...) renaming (...)
open import Foo2 using (...) renaming (...)
...
somewhat use "open ... public",
-- and do most of the import by `classes' and instances.
I hope,
this will join and re-export the needed things.
I used this approach in Haskell to create a union DPrelude of
MyProperPrelude and a large part of Standard library.
How people think, will a similar approach work in Agda?
(And as to my personal taste, I (currently) prefer to specify explicitly
what is imported from each small module, because this way it is easier
to control, to debug, to maintain
).
Regards,
------
Sergei
More information about the Agda
mailing list