Fold and map at the same time - functional-programming

I have a function
f : a -> b -> ( a, c )
and I need to apply f to a list of b, while a is accumulated and c is appended to a list, getting ( a, List c ). I think the signature of what I want to do is
(a -> b -> ( a, c )) -> a -> List b -> ( a, List c )
The real scenario here is that I have
getThing : Model -> Thing -> ( Model, Cmd Msg )
and need to run getThing on a list of Thing, passing the Model to each call to getThing and returning the model and all Cmds that are to be performed in a Platform.Cmd.batch.
I think this problem should be broken down into multiple parts, but I am not sure where to start. It feels like using a fold is appropriate for the Model, but I need a map for the Cmd part.

You just need to unpack the tuple returned by getThing in each iteration of the fold, then pack it back up with the command added to the accumulated list of commands as the accumulator.
mapThings : (a -> b -> ( a, c )) -> a -> List b -> ( a, List c )
mapThings getThing initialModel things =
List.foldl
(\thing ( model, cmds ) ->
let
( newModel, cmd ) =
getThing model thing
in
( newModel, cmd :: cmds )
)
( initialModel, [] )
things
The naming here is very specific to help mnemonics, but it can easily be generalized just by using more generic variable names.

If you don't mind pulling in another package, there's elm-community/list-extra List.Extra.mapAccuml and List.Extra.mapAccumr
https://package.elm-lang.org/packages/elm-community/list-extra/latest/List-Extra#mapAccuml

Related

How do I initialize a type alias like this in Elm?

type alias Bag a = List (a, Int)
I am trying to create functions that work with this type alias but I can't figure out how to return a Bag from a function.. Like in the following function I am hoping to insert a new item into an existing bag (could be empty, which is the only case I am trying to get working at the moment).
insert : a -> Bag a -> Bag a
insert b c = case c of
[] -> Bag a : [(b, 1)]
--this case below is what I'm hoping could work out..cant really check without getting the first case to work---
x::xs -> if Tuple.first x == b
then Bag a : [(b, (Tuple.second x) + 1)] ++ xs
else [x] ++ insert b xs
How can I initialize a new Bag without using this multi-line method below?
testBag : Bag Int
testBag = [(5,1),(2,1)]
PS very new to Elm and struggling to find resources that can help me shift from an imperative mindset..
You don't need to write Bag a: in front of bags. The compiler can deduce what type you need.
You do need to match up the indentation for the different branches of your case:
insert : a -> Bag a -> Bag a
insert b c = case c of
[] -> [(b, 1)]
x::xs -> if Tuple.first x == b
then [(b, (Tuple.second x) + 1)] ++ xs
else [x] ++ insert b xs
This works, but I wouldn't have written it that way. I'd have used pattern matching to split b into the item and count rather than Tuple.first and Tuple.second, and I'd use more descriptive names for the variables, giving:
add : a -> Bag a -> Bag a
add item bag =
case bag of
[] -> [ ( item, 1 ) ]
( firstItem, count ) :: otherItems ->
if item == firstItem then
( firstItem, count + 1 ) :: otherItems
else
( firstItem, count ) :: add item otherItems
I'd also define
emptyBag : Bag a
emptyBag = []
so that you can make
exampleBag : Bag Int
exampleBag = emptyBag |> add 5 |> add 3 |> add 5 |> add 3 |> add 3
By the way, most IDEs can run elm-format automatically for you when you save your work. That would lay out your code to be better maintainable and more easily readable. Other elm programmers would find it easier to read your code because it would be in the frequently-used elm layout.

Can't disambiguate name: Prelude.List.++, Prelude.Strings.++ error in Idris

Currently I am trying to create a function that takes the type of
(a -> a -> a) as a parameter in Idris and the right function to do is the ++ command for lists in idris unfortunately I am getting this error
ListMonoid : (A : Type) -> RawMonoid
ListMonoid A = (A ** ([], (++)) )
When checking right hand side of ListMonoid with expected type
RawMonoid
Can't disambiguate name: Prelude.List.++, Prelude.Strings.++
Raw Monoid is a data type below
RawMonoid : Type
RawMonoid = (M ** (M , M -> M -> M))
infixl 6 &
It seems to me that it does not know which ++ to use, is there a way to specify this in the call?
You can qualify the reference to (++), e.g.
ListMonoid A = (A ** ([], List.(++)) )
And there's also the with keyword, which takes a module name as its first argument - it basically means, "in the following expression, look in this module first to resolve names", e.g.
ListMonoid A = (A ** ([], with List (++)) )
However, both of those give me the following type error with your code:
Type mismatch between
List a -> List a -> List a (Type of (++))
and
A -> A -> A (Expected type)
If I write:
ListMonoid A = (List A ** ([], (++)) )
It's able to pick out the correct (++) based on the surrounding type constraints.

create a register

I have to create a type tree which would be used to store words, like every node of the tree would hold a letter and the list of the next characters (so words with the same root would share the same "part/branch of the tree). the tree is basically a n-ary one, used as a dictionnary.
All using Caml language
Well, I don't know if it's a homework or not but I'll still answer :
First, we need to define a signature type for letters.
module type LS = sig
type t
val compare : t -> t -> int
end
Then, we need to define our structure :
module Make (L : LS) = struct
module M = Map.Make(L)
type elt = L.t list
type t = { word : bool; branches : t M.t }
let empty = { word = false; branches = M.empty }
let is_empty t = not t.word && M.is_empty t.branches
let rec mem x t =
match x with
| [] -> t.word
| c :: cl -> try mem cl (M.find c t.branches)
with Not_found -> false
let rec add x t =
match x with
| [] -> if t.word then t else { t with word = true }
| c :: cl ->
let b = try M.find c t.branches with Not_found -> empty in
{ t with branches = M.add c (add cl b) t.branches }
end
Now, step by step :
module Make (L : LS) = struct is a functor that will return a new module if we give it a module of type LS as an argument
module M = Map.Make(L)
type elt = L.t list
type t = { word : bool; branches : t M.t }
This is the complex point, once you have it, everything begins clear. We need to represent a node (as you can see in the Wikipedia page of tries). My representation is this : a node is
a truth value stating that this node represent a word (which means that all the letters from the root to this node form a word)
the branches that goes from it. To represent this branches, I need a dictionary and luckily there's a Map functor in OCaml. So, my field branches is a field associating to some letters a trie (which is why I wrote that branches : t M.t). An element is then a list of letters and you'll find out why I chose this type rather than a string.
let empty = { word = false; branches = M.empty } the empty trie is the record with no branches (so, just the root), and this root is not a word (so word = false) (same idea for is_empty)
let rec mem x t =
match x with
| [] -> t.word
| c :: cl -> try mem cl (M.find c t.branches)
with Not_found -> false
Here it becomes interesting. My word being a list of letters, if I want to know if a word is in my trie, I need to make a recursive functions going through this list.
If I reached the point where my list is empty it means that I reached a node where the path from the root to it is composed by all the letters of my word. I just need to know, then, if the value word at this node is true or false.
If I still have at least one letter I need to find the branch corresponding to this letter.
If I find this branch (which will be a trie), I just need to make a recursive call to find the rest of the word (cl) in it
If I don't find it I know that my word doesn't exist in my trie so I can return false.
let rec add x t =
match x with
| [] -> if t.word then t else { t with word = true }
| c :: cl ->
let b = try M.find c t.branches with Not_found -> empty in
{ t with branches = M.add c (add cl b) t.branches }
Same idea. If I want to add a word :
If my list is empty it means that I added all the letters and I've reached the node corresponding to my word. In that case, if word is already true it means that this word was already added, I don't do anything. If word is false I just return the same branch (trie) but with word equal to true.
If my list contains at least a letter c, I find in the current node the branch corresponding to it (try M.find c t.branches with Not_found -> empty) and I there's no such branch, I just return an empty one and then I recursively add the rest of my letters to this branch and add this new branch to the branches of my current node associated to the letter c (if this branch already existed, it will be replaced since I use a dictionary)
Here, we start with the empty trie and we add the word to, top and tea.
In case we don't want to use functors, we can do it this way :
type elt = char list
type t = { word : bool; branches : (char * t) list }
let empty = { word = false; branches = [] }
let is_empty t = not t.word && t.branches = []
let find c l =
let rec aux = function
| [] -> raise Not_found
| (c', t) :: tl when c' = c -> t
| _ :: tl -> aux tl
in aux l
let rec mem x t =
match x with
| [] -> t.word
| c :: cl -> try mem cl (find c t.branches)
with Not_found -> false
let rec add x t =
match x with
| [] -> if t.word then t else { t with word = true }
| c :: cl ->
let b = try find c t.branches with Not_found -> empty in
{ t with branches = (c, (add cl b)) :: t.branches }

How to unfold a recursive function just once in Coq

Here is a recursive function all_zero that checks whether all members of a list of natural numbers are zero:
Require Import Lists.List.
Require Import Basics.
Fixpoint all_zero ( l : list nat ) : bool :=
match l with
| nil => true
| n :: l' => andb ( beq_nat n 0 ) ( all_zero l' )
end.
Now, suppose I had the following goal
true = all_zero (n :: l')
And I wanted to use the unfold tactic to transform it to
true = andb ( beq_nat n 0 ) ( all_zero l' )
Unfortunately, I can't do it with a simple unfold all_zero because the tactic will eagerly find and replace all instances of all_zero, including the one in the once-unfolded form, and it turns into a mess. Is there a way to avoid this and unfold a recursive function just once?
I know I can achieve the same results by proving an ad hoc equivalence with assert (...) as X, but it is inefficient. I'd like to know if there's an easy way to do it similar to unfold.
It seems to me that simpl will do what you want. If you have a more complicated goal, with functions that you want to apply and functions that you want to keep as they are, you might need to use the various options of the cbv tactic (see http://coq.inria.fr/distrib/current/refman/Reference-Manual010.html#hevea_tactic127).
Try
unfold all_zero; fold all_zero.
At least here for me that yields:
true = (beq_nat n 0 && all_zero l)%bool

Importance of isomorphic functions

Short Question: What is the importance of isomorphic functions in programming (namely in functional programming)?
Long Question: I'm trying to draw some analogs between functional programming and concepts in Category Theory based off of some of the lingo I hear from time-to-time. Essentially I'm trying to "unpackage" that lingo into something concrete I can then expand on. I'll then be able to use the lingo with an understanding of just-what-the-heck-I'm-talking about. Which is always nice.
One of these terms I hear all the time is Isomorphism, I gather this is about reasoning about equivalence between functions or function compositions. I was wondering if someone could provide some insights into some common patterns where the property of isomorphism comes in handy (in functional programming), and any by-products gained, such as compiler optimizations from reasoning about isomorphic functions.
I take a little issue with the upvoted answer for isomorphism, as the category theory definition of isomorphism says nothing about objects. To see why, let's review the definition.
Definition
An isomorphism is a pair of morphisms (i.e. functions), f and g, such that:
f . g = id
g . f = id
These morphisms are then called "iso"morphisms. A lot of people don't catch that the "morphism" in isomorphism refers to the function and not the object. However, you would say that the objects they connect are "isomorphic", which is what the other answer is describing.
Notice that the definition of isomorphism does not say what (.), id, or = must be. The only requirement is that, whatever they are, they also satisfy the category laws:
f . id = f
id . f = f
(f . g) . h = f . (g . h)
Composition (i.e. (.)) joins two morphisms into one morphism and id denotes some sort of "identity" transition. This means that if our isomorphisms cancel out to the identity morphism id, then you can think of them as inverses of each other.
For the specific case where the morphisms are functions, then id is defined as the identity function:
id x = x
... and composition is defined as:
(f . g) x = f (g x)
... and two functions are isomorphisms if they cancel out to the identity function id when you compose them.
Morphisms versus objects
However, there are multiple ways two objects could be isomorphic. For example, given the following two types:
data T1 = A | B
data T2 = C | D
There are two isomorphisms between them:
f1 t1 = case t1 of
A -> C
B -> D
g1 t2 = case t2 of
C -> A
D -> B
(f1 . g1) t2 = case t2 of
C -> C
D -> D
(f1 . g1) t2 = t2
f1 . g1 = id :: T2 -> T2
(g1 . f1) t1 = case t1 of
A -> A
B -> B
(g1 . f1) t1 = t1
g1 . f1 = id :: T1 -> T1
f2 t1 = case t1 of
A -> D
B -> C
g2 t2 = case t2 of
C -> B
D -> A
f2 . g2 = id :: T2 -> T2
g2 . f2 = id :: T1 -> T1
So that's why it's better to describe the isomorphism in terms of the specific functions relating the two objects rather than the two objects, since there may not necessarily be a unique pair of functions between two objects that satisfy the isomorphism laws.
Also, note that it is not sufficient for the functions to be invertible. For example, the following function pairs are not isomorphisms:
f1 . g2 :: T2 -> T2
f2 . g1 :: T2 -> T2
Even though no information is lost when you compose f1 . g2, you don't return back to your original state, even if the final state has the same type.
Also, isomorphisms don't have to be between concrete data types. Here's an example of two canonical isomorphisms are not between concrete algebraic data types and instead simply relate functions: curry and uncurry:
curry . uncurry = id :: (a -> b -> c) -> (a -> b -> c)
uncurry . curry = id :: ((a, b) -> c) -> ((a, b) -> c)
Uses for Isomorphisms
Church Encoding
One use of isomorphisms is to Church-encode data types as functions. For example, Bool is isomorphic to forall a . a -> a -> a:
f :: Bool -> (forall a . a -> a -> a)
f True = \a b -> a
f False = \a b -> b
g :: (forall a . a -> a -> a) -> Bool
g b = b True False
Verify that f . g = id and g . f = id.
The benefit of Church encoding data types is that they sometimes run faster (because Church-encoding is continuation-passing style) and they can be implemented in languages that don't even have language support for algebraic data types at all.
Translating Implementations
Sometimes one tries to compare one library's implementation of some feature to another library's implementation, and if you can prove that they are isomorphic, then you can prove that they are equally powerful. Also, the isomorphisms describe how to translate one library into the other.
For example, there are two approaches that provide the ability to define a monad from a functor's signature. One is the free monad, provided by the free package and the other is operational semantics, provided by the operational package.
If you look at the two core data types, they look different, especially their second constructors:
-- modified from the original to not be a monad transformer
data Program instr a where
Lift :: a -> Program instr a
Bind :: Program instr b -> (b -> Program instr a) -> Program instr a
Instr :: instr a -> Program instr a
data Free f r = Pure r | Free (f (Free f r))
... but they are actually isomorphic! That means that both approaches are equally powerful and any code written in one approach can be translated mechanically into the other approach using the isomorphisms.
Isomorphisms that are not functions
Also, isomorphisms are not limited to functions. They are actually defined for any Category and Haskell has lots of categories. This is why it's more useful to think in terms of morphisms rather than data types.
For example, the Lens type (from data-lens) forms a category where you can compose lenses and have an identity lens. So using our above data type, we can define two lenses that are isomorphisms:
lens1 = iso f1 g1 :: Lens T1 T2
lens2 = iso g1 f1 :: Lens T2 T1
lens1 . lens2 = id :: Lens T1 T1
lens2 . lens1 = id :: Lens T2 T2
Note that there are two isomorphisms in play. One is the isomorphism that is used to build each lens (i.e. f1 and g1) (and that's also why that construction function is called iso), and then the lenses themselves are also isomorphisms. Note that in the above formulation, the composition (.) used is not function composition but rather lens composition, and the id is not the identity function, but instead is the identity lens:
id = iso id id
Which means that if we compose our two lenses, the result should be indistinguishable from that identity lens.
An isomorphism u :: a -> b is a function that has an inverse, i.e. another function v :: b -> a such that the relationships
u . v = id
v . u = id
are satisfied. You say that two types are isomorphic if there is an isomorphism between them. This essentially means that you can consider them to be the same type - anything that you can do with one, you can do with the other.
Isomorphism of functions
The two function types
(a,b) -> c
a -> b -> c
are isomorphic, since we can write
u :: ((a,b) -> c) -> a -> b -> c
u f = \x y -> f (x,y)
v :: (a -> b -> c) -> (a,b) -> c
v g = \(x,y) -> g x y
You can check that u . v and v . u are both id. In fact, the functions u and v are better known by the names curry and uncurry.
Isomorphism and Newtypes
We exploit isomorphism whenever we use a newtype declaration. For example, the underlying type of the state monad is s -> (a,s) which can be a little confusing to think about. By using a newtype declaration:
newtype State s a = State { runState :: s -> (a,s) }
we generate a new type State s a which is isomorphic to s -> (a,s) and which makes it clear when we use it, we are thinking about functions that have modifiable state. We also get a convenient constructor State and a getter runState for the new type.
Monads and Comonads
For a more advanced viewpoint, consider the isomorphism using curry and uncurry that I used above. The Reader r a type has the newtype declaration
newType Reader r a = Reader { runReader :: r -> a }
In the context of monads, a function f producing a reader therefore has the type signature
f :: a -> Reader r b
which is equivalent to
f :: a -> r -> b
which is one half of the curry/uncurry isomorphism. We can also define the CoReader r a type:
newtype CoReader r a = CoReader { runCoReader :: (a,r) }
which can be made into a comonad. There we have a function cobind, or =>> which takes a function that takes a coreader and produces a raw type:
g :: CoReader r a -> b
which is isomorphic to
g :: (a,r) -> b
But we already saw that a -> r -> b and (a,r) -> b are isomorphic, which gives us a nontrivial fact: the reader monad (with monadic bind) and the coreader comonad (with comonadic cobind) are isomorphic as well! In particular, they can both be used for the same purpose - that of providing a global environment that is threaded through every function call.
Think in terms of datatypes. In Haskell for example you can think of two data types to be isomorphic, if there exists a pair of functions that transform data between them in a unique way. The following three types are isomorphic to each other:
data Type1 a = Ax | Ay a
data Type2 a = Blah a | Blubb
data Maybe a = Just a | Nothing
You can think of the functions that transform between them as isomorphisms. This fits with the categorical idea of isomorphism. If between Type1 and Type2 there exist two functions f and g with f . g = g . f = id, then the two functions are isomorphisms between those two types (objects).

Resources