Related
I know that I can run monadic instructions sequentially inside monads in Kind language, like this:
Test: _
IO {
IO.print(Nat.show(2))
IO.print(Nat.show(3))
IO.print(Nat.show(4))
}
output:
2
3
4
But is it possible to run monadic instructions repeatedly, like this below?
Test: _
a = [2,1,3,4,5]
IO {
for num in a:
IO.print(Nat.show(num))
}
If it is possible how can I do it correctly?
Monads are usually represented by only two operators :
return :: a -> m(a) // that encapulapse the value inside a effectful monad
>>= :: m a -> (a -> m b) -> m b
// the monadic laws are omitted
Notice, the bind operator is naturally recursive, once it can compose two monads or even discard the value of one and the return can be thought of as a "base case".
m >>= (\a -> ... >>= (\b -> ~ i have a and b, compose or discard? ~) >>= fixpoint)
You just have to produce that sequence, which is pretty straightforward. For example, in Kind we represent monads as a pair which takes a type-for-type value and encapluse a polymorphic type.
type Monad <M: Type -> Type> {
new(
bind: <A: Type, B: Type> M<A> -> (A -> M<B>) -> M<B>
pure: <A: Type> A -> M<A>
)
}
In your example, we just have to trigger the effect and discard the value, a recursive definition is enough :
action (x : List<String>): IO(Unit)
case x {
nil : IO.end!(Unit.new) // base case but we are not worried about values here, just the effects
cons : IO {
IO.print(x.head) // print and discard the value
action(x.tail) // fixpoint
}
}
test : IO(Unit)
IO {
let ls = ["2", "1", "3", "4", "5"]
action(ls)
}
The IO as you know it will be desugared by a sequence of binds!
Normally in case of list it can be generalized like the mapM function of haskell library :
Monadic.forM(A : Type -> Type, B : Type,
C : Type, m : Monad<A>, b : A(C), f : B -> A(C), x : List<A(B)>): A(C)
case x {
nil : b
cons :
open m
let k = App.Kaelin.App.mapM!!!(m, b, f, x.tail)
let ac = m.bind!!(x.head, f)
m.bind!!(ac, (c) k) // the >> operator
}
It naturally discard the value and finally we can do it :
action2 (ls : List<String>): IO(Unit)
let ls = [IO.end!(2), IO.end!(1), IO.end!(3), IO.end!(4), IO.end!(5)]
Monadic.forM!!!(IO.monad, IO.end!(Unit.new), (b) IO.print(Nat.show(b)), ls)
So, action2 do the same thing of action, but in one line!.
When you need compose the values you can represent as monadic fold :
Monadic.foldM(A : Type -> Type, B : Type,
C : Type, m : Monad<A>, b : A(C), f : B -> C -> A(C), x : List<A(B)>): A(C)
case x {
nil : b
cons :
open m
let k = Monadic.foldM!!!(m, b, f, x.tail)
m.bind!!(x.head, (b) m.bind!!(k, (c) f(b, c)))
}
For example, suppose that you want to sum a sequence of numbers that you ask for the user in a loop, you just have to call foldM and compose with a simple function :
Monad.action3 : IO(Nat)
let ls = [IO.get_line, IO.get_line, IO.get_line]
Monadic.foldM!!!(IO.monad, IO.end!(0),
(b, c) IO {
IO.end!(Nat.add(Nat.read(b), c))
},
ls)
test : IO(Unit)
IO {
get total = action3
IO.print(Nat.show(total))
}
For now, Kind do not support typeclass so it make the things a little more verbose, but i think a new support to forM loops syntax can be thought in the future. We hope so :)
I've got a list of items that have to be rendered. I have a function called viewItem that can render one item. I do a simple List.map viewItem items and now I have a list of items that can be displayed.
My view has four columns. How can I split this list into four lists that contain all of the elements from my original list?
This is how I'm doing it now, but there has to be something I'm missing. I want to be able to split it into five columns or even six without having to write col4 = ... and col5 = ... every time.
splitColumns : Int -> Array a -> Array (List a)
splitColumns cnum xs =
let
ixdList =
Array.toIndexedList xs
in
List.filterMap
(\a ->
if modBy 4 (Tuple.first a) == cnum then
Just (Tuple.second a)
else
Nothing
)
ixdList
viewItems : Array Item -> Html msg
viewItems items =
let
itemsHtml =
Array.map viewItem items
col0 =
splitColumns 0 itemsHtml
col1 =
splitColumns 1 itemsHtml
col2 =
splitColumns 2 itemsHtml
col3 =
splitColumns 3 itemsHtml
in
main_
[ class "section" ]
[ Html.div
[ class "container" ]
[ Html.div
[ class "columns" ]
[ Html.div
[ class "column" ]
col0
, Html.div
[ class "column" ]
col1
, Html.div
[ class "column" ]
col2
, Html.div
[ class "column" ]
col3
]
]
]
You could rewrite your current approach as a fold that only does one pass like this:
cols : List a -> { col0 : List a, col1 : List a, col2 : List a, col3 : List a }
cols list =
list
|> List.foldl
(\x ( i, cols ) ->
case modBy 4 i of
0 ->
( i + 1, { cols | col0 = x :: cols.col0 } )
1 ->
( i + 1, { cols | col1 = x :: cols.col1 } )
2 ->
( i + 1, { cols | col2 = x :: cols.col2 } )
3 ->
( i + 1, { cols | col3 = x :: cols.col3 } )
_ ->
( i + 1, cols )
)
( 0, { col0 = [], col1 = [], col2 = [], col3 = [] } )
|> Tuple.second
This also keeps track of the index internally, so it doesn't require that you give it an indexed list, but it's still hard-coded for four columns. If we want to be able to use it with an arbitrary number of columns, we have to use a data structure that can hold an arbitrary number of items in sequence. An array is perfect for this, allowing us to update it with an index computed using modBy:
cols : Int -> List a -> List (List a)
cols n list =
list
|> List.foldl
(\x ( i, cols ) ->
let
index =
modBy n i
tail =
cols |> Array.get index |> Maybe.withDefault []
in
( i + 1, Array.set index (x :: tail) cols )
)
( 0, Array.repeat n [] )
|> Tuple.second
|> Array.toList
We can then use List.map in the view function to render them:
viewItems : Array Item -> Html msg
viewItems items =
let
itemsHtml =
Array.map viewItem items
|> Array.toList
in
main_
[ class "section" ]
[ Html.div
[ class "container" ]
[ Html.div
[ class "columns" ]
(cols 4 itemsHtml |> List.map (Html.div [ class "column" ]))
]
]
You can use List.map with List.range. List.range a b generates a list of integers from a to b (inclusive).
Your viewItems function is greatly simplified by this:
viewItems : Array Item -> Html msg
viewItems items =
main_
[ class "section" ]
[ Html.div
[ class "container" ]
[ Html.div
[ class "columns" ]
List.map (\n -> Html.div [ class "column" ] (splitColumns n (Array.map viewItem items)) (List.range 0 3)
]
]
If you want to support a different number of columns, you can of course replace the hard-coded 4 in splitColumns with a parameter.
I am reletively new to functional programming, to be honest about 2 days. I am trying to print out values from a Dict Int Int, but I can't seem to figure out how to pass this Dict Int Int to a function.
The error I am receiving is below.
The 1st argument to map is not what I expect:
32| div [] (Dict.map toLiDict dict)
^^^^^^^^ This toLiDict value is a:
Dict Int Int -> Html msg
But map needs the 1st argument to be:
Dict Int Int -> b
The function toHtmlDict and toLiDict is the one's causing me this issue, they are currently commented out below. Also I am calling this from the view, div [] [ toHtmlDict model.uniqueValues ], I have this commented out as well.
Here is what I am currently working with; I posted the whole code as it would be easier if you should need anything else.
Here's a link on Ellie here that can be ran.
module Main exposing (main)
import Browser
import Dict exposing (Dict)
import Html.Attributes
import Html exposing (Html, button, div, text, strong, p, li, ul)
import Html.Events exposing (onClick)
type alias Model =
{ currentNumber : Int, clicks : Int, outputList : List(String), uniqueValues : Dict Int Int }
--{ currentNumber : Int, clicks : Int, history : String, outputList : List(String) }
initialModel : Model
initialModel =
{ currentNumber = 0, clicks = 0, outputList = [""], uniqueValues = Dict.fromList [(1,1)] } --Dict.empty should be default here...
--{ currentNumber = 0, clicks = 0, history = "Current outputs ...", outputList = ["Current outputs ...", " "] }
-- applies a new div for each element in the list
toHtmlList : List String -> Html msg
toHtmlList strings =
div [] (List.map toLi strings)
-- creates a div along with the text to be shown
toLi : String -> Html msg
toLi s =
div [] [ text s ]
-- --applies a new div for each element in the dictionary
-- toHtmlDict : Dict Int Int -> Html msg
-- toHtmlDict dict =
-- div [] (Dict.map toLiDict dict)
-- -- creates a div along with the text to be shown
-- toLiDict : Dict Int Int -> Html msg
-- toLiDict k =
-- div [] [ text "What here?" ]
type Msg
= Increment
| Decrement
update : Msg -> Model -> Model
update msg model =
case msg of
-- Note: when assigning the model.currentNumber and then concatenating the value, it will not be updated...
Increment ->
{ model | currentNumber = model.currentNumber + 1, clicks = model.clicks + 1, outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1)], uniqueValues = model.uniqueValues }
--{ model | currentNumber = model.currentNumber + 1, clicks = model.clicks + 1, history = model.history ++ addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1), outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber + 1)) (model.currentNumber + 1)] }
Decrement ->
{ model | currentNumber = model.currentNumber - 1, clicks = model.clicks + 1, outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1)]}
--{ model | currentNumber = model.currentNumber - 1, clicks = model.clicks + 1, history = model.history ++ addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1), outputList = model.outputList ++ [addToPage (oddOrEven(model.currentNumber - 1)) (model.currentNumber - 1)]}
view : Model -> Html Msg
view model =
Html.div []
[ button [ onClick Increment ] [ strong [Html.Attributes.style "color" "black"] [ text "+1" ]]
, button [ onClick Decrement ] [ strong [Html.Attributes.style "color" "red"] [ text "-1" ]]
, p [] []
--, div [] [ text <| "The current number is: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.currentNumber ], text " and it's ", text (oddOrEven model.currentNumber) ]
, div [] [ text <| "The current number is: ", strong [Html.Attributes.style "color" <| evenOddColor model.currentNumber] [ text <| String.fromInt model.currentNumber ], text " and it's ", strong [Html.Attributes.style "color" <| evenOddColor model.currentNumber ] [ text <| oddOrEven model.currentNumber ] ]
, div [] [ text "Total clicks: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.clicks ]]
, p [] []
, div [] [ strong [Html.Attributes.style "color" "Blue"] [ text "Unique values ..." ]]
, p [] []
--, div [] [ toHtmlDict model.uniqueValues ]
--, div [] [ text <| "The current number is: " ++ String.fromInt model.currentNumber ++ " and it's " ++ oddOrEven model.currentNumber ]
--, div [] [ text "Total clicks: ", strong [Html.Attributes.style "color" "red"] [ text <| String.fromInt model.clicks ]]
--, p [] []
, div [] [ strong [Html.Attributes.style "color" "Blue"] [ text "History ..." ]]
, p [] []
--, div [] [ text <| model.history ] - TEMPORARY
, div [] [ toHtmlList model.outputList ]
]
-- appendToList string number =
-- "Number was " ++ String.fromInt number ++ " and it was " ++ string
addToPage string number =
"Number was " ++ String.fromInt number ++ " and it was " ++ string
-- determines if number is even or odd
oddOrEven number =
if modBy 2 number == 0 then
"even"
else
"odd"
-- call another function with param
evenOddColor number =
if oddOrEven(number) == "even" then
"green"
else
"red"
main : Program () Model Msg
main =
Browser.sandbox
{ init = initialModel
, view = view
, update = update
}
Dict.map is a function that will transform the values of a Dict and return another Dict with the new values. This is not what you want here, but lets try to grok its type anyway, because it's a useful learning experience.
Dict.map
A Dicts full type is Dick k v, meaning that the type variables correspond to the type of its keys and values respectively. Dict.map, according to the documentation, has the type (k -> a -> b) -> Dict k a -> Dict k b. As its first argument it takes a function of two arguments, k and a, and that returns a b. maps second argument is a Dict k a, and it returns a Dict k b.
We can see that the k is the same in both the input and return Dicts, which means its type will remain the same, but the type variable for the values is different, a in the input and b in the return Dict. And the function similarly takes a as one of its inputs, along with a k, and returns a b. So for each of the key-value pairs in the input Dict, the mapping function will be called with its key, 'k', and value 'a', and is expected to return a b value.
For a Dict Int Int as you have, both k and v are Ints. If we substitute these in the type of Dict.map we get (Int -> Int -> b) -> Dict Int Int -> Dict Int b. We don't know what b is yet since that will be determined by the function we pass it, but we can at least see that the function expects two Int arguments`.
Meanwhile, the function you give it, toLiDict, has the type Dict Int Int -> Html msg which takes one argument that isn't an Int. This is what the error message is clumsily trying to convey. We could rewrite toLiDict to conform to what Dict.map expects, as a function Int -> Int -> Html msg, but that would have Dict.map return a Dict Int (Html msg), which isn't what you want. So let's drop that.
In general, map functions conventionally transform the elements of a collection without changing the type of collection.
Dict.foldl
If you want instead is to transform the elements of a collection into something else entirely, and there isn't something more specific to use, a "fold" is usually the right tool. Dict provides foldl and foldr, which basically does the same thing but in different order, foldl iterates over the elements from the "left" and foldr from the "right". If the order doesn't matter, use foldl because it's more efficient.
The type signature of Dict.foldl is (k -> v -> b -> b) -> b -> Dict k v -> b. That is, the transformation function now takes three arguments, the key, k, value, v, and a b which we'll call the accumulator, and returns a b. foldl itself also takes an additional argument, again a b, which will be the initial b value passed to the transformation function. The third argument is the Dict, and the return value is again a b.
For each key-value pair in the input Dict, foldl will, just like map, call the transformation function with its key and value. But it also provides a b which initially is the b value passed to foldl itself, but for subsequent iterations will be the b value returned from the transformation function. In that way the "accumulator" accumulates the return value.
Let's rewrite your code to use Dict.foldl instead:
toHtmlDict : Dict Int Int -> Html msg
toHtmlDict dict =
div [] (Dict.foldl toLiDict [] dict)
toLiDict : Int -> Int -> List (Html msg) -> List (Html msg)
toLiDict k v acc =
div [] [ text "What here?" ] :: acc
Here, toHtmlDict remains largely the same, but uses Dict.foldl instead of Dict.map and provides it an initial value of an empty list, [].
toLiDict sees bigger changes. Its type has changed to Int -> Int -> List (Html msg) -> List (Html msg), mneaning it takes the arguments: The key and value, both of which are Ints, and the accumulator is a List (Html msg), as is the return value.
But the implementation has barely changed. Instead of just returning an element directly, it's appended to the accumulator with :: acc.
And that's all there is to it. The result of the fold is the accumulated list of Html elements, as expected. If you plop the above code into yours, it will work.
Dict.values and Dict.toList
Finally, I noted earlier that foldl is a good choice if there aren't more appropriate specialized function. And since the end result you want is a list, either Dict.values or Dict.toList, as #bdukes has suggested, probably are. These aren't as efficient as a fold, since you'll iterate trough the elements twice, once to convert to a list and then to map them, but this rarely matters in practice. Specialized functions are also more descriptive and documents your intent better, so use them if you can.
The definition of Dict.map is (k -> a -> b) -> Dict k a -> Dict k b. So it takes a function and a Dict and returns a new Dict. That mapping function takes the key and value, and returns a new value.
In your case, you're looking to return a List (Html Msg), rather than a Dict of anything. So, rather than using Dict.map, I would call Dict.values to get a List of the values, and then use List.map to transform those values into Html Msg. If you need both the key and value to generate the Html Msg, use Dict.toList instead, to get a List (k, v) (i.e. a List of tuples, where the tuple has the key and value).
toHtmlDict : Dict Int Int -> Html Msg
toHtmlDict dict =
div [] (List.map viewDictEntry (Dict.toList dict))
viewDictEntry : (Int, Int) -> Html Msg
viewDictEntry (key, value) =
li [] [ text (String.fromInt key), text " = ", text (String.fromInt value) ]
I just want to know if there's a clean way to get the "other" element from a map in Elixir. By "other" I mean a second key-value pair, whose key I don't know.
Example: %{success: boolean, other => value}
This is the best I could come up with:
case map do
%{success: true} ->
other = map |> Map.delete(:success) |> Map.values |> List.first
# Do something with other
%{success: false} ->
error = map |> Map.delete(:success) |> Map.values |> List.first
# Do something with error
end
There's Map.pop/3 function, which accepts map and a key and returns a tuple with the value and a map without the key:
Map.pop %{ a: 1, b: 2 }, :a
# => {1, %{b: 2}}
and will refactor your code into something like:
case Map.pop(map, :success) do
{true, other_map} ->
other = other_map |> Map.values |> List.first
# Do something with other
{false, other_map} ->
error = other_map |> Map.values |> List.first
# Do something with error
end
I would go with old good Enum.reduce/3:
Enum.reduce %{success: true, foo: 42}, %{state: nil, map: %{}}, fn
{:success, value}, acc -> %{acc | state: value}
{key, value}, acc -> %{acc | map: Map.put(acc.map, key, value)}
end
#⇒ %{map: %{foo: 42}, state: true}
Now you might do whatever is needed without code duplication. Actually, the tuple is fine for collecting the result:
{success, map} =
Enum.reduce %{success: true, foo: 42}, {nil, %{}}, fn
{:success, value}, {_, acc} -> {value, acc}
{key, value}, {state, acc} -> {state, Map.put(acc, key, value)}
end
#⇒ {true, %{foo: 42}}
iex(9)> map = %{:success => true, {1,2,3} => 10}
%{:success => true, {1, 2, 3} => 10}
iex(10)> List.first(for {key, val} <- map, key != :success, do: val)
10
I have the following code:
digraph g {
graph [rankdir="LR" ,compound="true" ];
subgraph cluster0 {
graph [label="Ready\n\nAllowed Purchaser Operations:\noperation1,operation2\n\nAllowed Supplier Operations:\noperation1,operation3" ];
1 [ shape="none" ,fontcolor="white" ];
};
subgraph cluster2 {
graph [label="Paused\n\nAllowed Purchaser Operations:\noperation1,operation3\n\nAllowed Supplier Operations:\noperation2,operation3" ];
3 [ shape="none" ,fontcolor="white" ];
};
subgraph cluster4 {
graph [label="Completed\n\nAllowed Purchaser Operations:\noperation4\n\nAllowed Supplier Operations:\noperation4" ];
5 [ shape="none" ,fontcolor="white" ];
};
1 -> 3 [ ltail="cluster0" ,lhead="cluster2" ,comment="6" ];
1 -> 5 [ ltail="cluster0" ,lhead="cluster4" ,comment="7" ];
3 -> 1 [ ltail="cluster2" ,lhead="cluster0" ,comment="8" ];
3 -> 5 [ ltail="cluster2" ,lhead="cluster4" ,comment="9" ];
}
I want to increase the distance between the subgraphs. I've tried using len, margin, pad, but the syntax I've tried doesn't work. Can somebody help me?
I think what you are looking for (as Emden points out) are indeed the nodesep and ranksep attributes.
graph [nodesep=6, ranksep=4];
The result would be:
Clusters are derived objects; their layout depends solely on the nodes contained in them. Thus, to alter the cluster spacing, you need to alter the node spacing. Try setting the ranksep and nodesep attributes to larger values.