Elm: populating a record with List values - recursion

Trying to learn Elm, and it's pretty hard :)
What I'm trying to accomplish:
I have a model that's a record with a number of key value pairs. I want to populate those keys with values from a list of strings.
module Main exposing (..)
import List
import Html exposing (Html, program, div, text)
type alias Model =
{ one: String
, two: String
, three: String
}
fakeData: List String
fakeData = ["foo", "bar", "baz", "bad", "baf"]
populate: Model -> List String -> Model
populate model data =
case List.head data of
Just str ->
case model.one of
"" ->
let updatedModel =
{ model | one = str }
in
case List.tail data of
Just items ->
populate updatedModel items
Nothing ->
model
_ ->
case model.two of
"" ->
let updatedModel =
{ model | two = str }
in
case List.tail data of
Just items ->
populate updatedModel items
Nothing ->
model
_ ->
case model.three of
"" ->
let updatedModel =
{ model | three = str }
in
case List.tail data of
Just items ->
populate updatedModel items
Nothing ->
model
_ ->
model
Nothing ->
model
init: ( Model, Cmd Msg)
init =
( populate { one = "", two = "", three = "" } fakeData, Cmd.none )
type Msg =
NoOp
view: Model -> Html Msg
view model =
div []
[ text (toString model) ]
update: Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NoOp ->
( model, Cmd.none )
subscriptions: Model -> Sub Msg
subscriptions model =
Sub.none
main: Program Never Model Msg
main =
program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
This program prints out:
{ one = "foo", two = "bar", three = "baz" }
I guess I have a hard time figuring out how to make this code less repetitive and easier to reason about. What if I have 20 keys in the model that all need to be populated? The above code would get pretty insane.

You could use pattern matching on the list instead:
populate : Model -> List String -> Model
populate model data =
case data of
one :: two :: three :: _ ->
{ model | one = one, two = two, three = three }
_ ->
model

Related

Access the contents of the row inserted into Dynamodb using Pynamodb save method

I have the below model for a my dynamodb table using pynamodb :
from pynamodb.models import Model
from pynamodb.attributes import (
UnicodeAttribute, UTCDateTimeAttribute, UnicodeSetAttribute, BooleanAttribute
)
class Reminders(Model):
"""Model class for the Reminders table."""
# Information on global secondary index for the table
# user_id (hash key) + reminder_id+reminder_title(sort key)
class Meta:
table_name = 'Reminders'
region = 'eu-central-1'
reminder_id = UnicodeAttribute(hash_key=True)
user_id = UnicodeAttribute(range_key=True)
reminder_title = UnicodeAttribute()
reminder_tags = UnicodeSetAttribute()
reminder_description = UnicodeAttribute()
reminder_frequency = UnicodeAttribute(default='Only once')
reminder_tasks = UnicodeSetAttribute(default=set())
reminder_expiration_date_time = UTCDateTimeAttribute(null=True)
reminder_title_reminder_id = UnicodeAttribute()
next_reminder_date_time = UTCDateTimeAttribute()
should_expire = BooleanAttribute()
When i want to create a new reminder i do it through the below code :
class DynamoBackend:
#staticmethod
def create_a_new_reminder(new_reminder: NewReminder) -> Dict[str, Any]:
"""Create a new reminder using pynamodb."""
new_reminder = models.Reminders(**new_reminder.dict())
return new_reminder.save()
In this case the NewReminder is an instance of pydantic base model like so :
class NewReminder(pydantic.BaseModel):
reminder_id: str
user_id: str
reminder_title: str
reminder_description: str
reminder_tags: Sequence[str]
reminder_frequency: str
should_expire: bool
reminder_expiration_date_time: Optional[datetime.datetime]
next_reminder_date_time: datetime.datetime
reminder_title_reminder_id: str
when i call the save method on the model object i receive the below response:
{
"ConsumedCapacity": {
"CapacityUnits": 2.0,
"TableName": "Reminders"
}
}
Now my question is the save method is directly being called by a lambda function which is in turn called by an API Gateway POST endpoint so ideally the response should be a 201 created and instead of returning the consumed capacity and table name , would be great if it returns the item inserted in the database. Below is my route code :
def create_a_new_reminder():
"""Creates a new reminder in the database."""
request_context = app.current_request.context
request_body = json.loads(app.current_request.raw_body.decode())
request_body["reminder_frequency"] = data_structures.ReminderFrequency[request_body["reminder_frequency"]]
reminder_details = data_structures.ReminderDetailsFromRequest.parse_obj(request_body)
user_details = data_structures.UserDetails(
user_name=request_context["authorizer"]["claims"]["cognito:username"],
user_email=request_context["authorizer"]["claims"]["email"]
)
reminder_id = str(uuid.uuid1())
new_reminder = data_structures.NewReminder(
reminder_id=reminder_id,
user_id=user_details.user_name,
reminder_title=reminder_details.reminder_title,
reminder_description=reminder_details.reminder_description,
reminder_tags=reminder_details.reminder_tags,
reminder_frequency=reminder_details.reminder_frequency.value[0],
should_expire=reminder_details.should_expire,
reminder_expiration_date_time=reminder_details.reminder_expiration_date_time,
next_reminder_date_time=reminder_details.next_reminder_date_time,
reminder_title_reminder_id=f"{reminder_details.reminder_title}-{reminder_id}"
)
return DynamoBackend.create_a_new_reminder(new_reminder=new_reminder)
I am very new to REST API creation and best practices so would be great if someone would guide me here . Thanks in advance !

Why is is the compiler telling me "Type misMatch for App message" when they are the same type

So, I've been fighting with the compiler on a type error.
This code was working as of a couple days ago.
Type misMatch for App level message
App.fs snippets
module App =
type Msg =
| ConnectionPageMsg of ConnectionPage.Msg
| CodeGenPageMsg of CodeGenPage.Msg
//...
let update (msg : Msg) (model : Model) =
match msg with
| ConnectionPageMsg msg ->
let m, cmd = ConnectionPage.update msg model.ConnectionPageModel
{ model with ConnectionPageModel = m }, cmd
| CodeGenPageMsg msg ->
let m, cmd = CodeGenPage.update msg model.CodeGenPageModel
{ model with CodeGenPageModel = m }, cmd
//...
let runner =
Program.mkProgram init update view
|> Program.withConsoleTrace
|> XamarinFormsProgram.run app
I've added explicit aliases and the original error :
Type mismatch. Expecting a
'App.Msg -> App.Model -> App.Model * Cmd<App.Msg>'
but given a
'App.Msg -> App.Model -> App.Model * Cmd<Msg>'
The type 'App.Msg' does not match the type 'Msg'
Became these:
App.fs(50,50): Error FS0001: The type 'PocoGen.Page.ConnectionPage.Msg' does not match the type 'PocoGen.Page.CodeGenPage.Msg' (FS0001) (PocoGen)
App.fs(32,32): Error FS0001: Type mismatch.
Expecting a 'App.Msg -> App.Model -> App.Model * Cmd<App.Msg>'
but given a 'App.Msg -> App.Model -> App.Model * Cmd<Msg>'
The type 'App.Msg' does not match the type 'Msg' (FS0001) (PocoGen)
Other remarks
Right before these errors started appearing I was working on converting a blocking syncronous call to a async command in the ConnectionTestPage and removed the calling code for the cmd hoping that would fix it. (It did not)
ConnectionPage.fs Messages
type Msg =
| UpdateConnectionStringValue of string
| UpdateConnectionStringName of string
| TestConnection
| TestConnectionComplete of Model
| SaveConnectionString of ConnectionStringItem
| UpdateOutput of string
ConnectionPage.fs update
let update (msg : Msg) (m : Model) : Model * Cmd<Msg> =
match msg with
| UpdateConnectionStringValue conStringVal ->
{ m with
ConnectionString =
{ Id = m.ConnectionString.Id
Name = m.ConnectionString.Name
Value = conStringVal }
CurrentFormState =
match hasRequredSaveFields m.ConnectionString with
| false -> MissingConnStrValue
| _ -> Valid }, Cmd.none
| UpdateConnectionStringName conStringName ->
{ m with
ConnectionString =
{ Id = m.ConnectionString.Id
Name = conStringName
Value = m.ConnectionString.Value }
CurrentFormState =
match hasRequredSaveFields m.ConnectionString with
| false -> MissingConnStrValue
| _ -> Valid }, Cmd.none
| UpdateOutput output -> { m with Output = output }, Cmd.none
| TestConnection -> m, Cmd.none
| TestConnectionComplete testResult -> { m with Output = testResult.Output + "\r\n" }, Cmd.none
| SaveConnectionString(_) -> saveConnection m, Cmd.none
I've played with the Fsharp Version (because incidentally I did update to 4.7.2 a bit before getting this error)
The Full Repo:
https://github.com/musicm122/PocoGen_Fsharp/tree/master/PocoGen
The two branches of the match inside App.update have different types. The first branch has type App.Model * Cmd<ConnectionPage.Msg> and the second page has type App.Model * Cmd<CodeGenPage.Msg>.
You can't generally do that. This, for example, wouldn't compile:
let x =
match y with
| true -> 42
| false -> "foo"
What type is x here? Is it int or is it string? Doesn't compute. A match expression has to have all branches of the same type.
To convert Cmd<ConnectionPage.Msg> into a Cmd<App.Msg> (by wrapping the message in ConnectionPageMsg) you can use Cmd.map:
let update (msg : Msg) (model : Model) =
match msg with
| ConnectionPageMsg msg ->
let m, cmd = ConnectionPage.update msg model.ConnectionPageModel
{ model with ConnectionPageModel = m }, Cmd.map ConnectionPageMsg cmd
| CodeGenPageMsg msg ->
let m, cmd = CodeGenPage.update msg model.CodeGenPageModel
{ model with CodeGenPageModel = m }, Cmd.map CodeGenPageMsg cmd

Is there a way to check nested option values in one pattern in F#?

Let's pretend we have the following types:
type Message {
text : Option<string>
}
type Update {
msg : Option<Message>
}
How do I match it in one line, like in C# using null-conditional operator i.e update?.msg?.text ?
Is there a way to do it like this?:
match msg, msg.text with
| Some msg, Some txt -> ...
| None -> ...
because I don't want to be writing 2 nested match expressions.
You have two Record types (missing the "=" in your example). To match some variable of Update type, you could do as follows:
type Message = { text : Option<string> }
type Update = { msg : Option<Message> }
let u = {msg = Some({text = Some "text"})}
//all 3 possible cases
match u with
| {msg = Some({text = Some t})} -> t
| {msg = Some({text = None})} -> ""
| {msg = None} -> ""

Creating Expression Tree Kotlin

Hi my task is to recursively create an expression tree from a List input.
ex. ("+", 2, 3)
"+"
/ \
2 3
I know my recursion is off, but I can't figure out how to make it work. I've been at it for longer than I'm proud to admit. Any insight is appreciated! This is what I have so
fun TreeBuild(input:List<Any>): Pair<Tree, List<Any>>{
var tree = Tree(input.get(0), Unit, Unit)
var list = input
System.out.println(list)
if(input.size == 1 ){
list = list.drop(1)
return Pair(Tree(input.get(0), Unit, Unit), list)
}
var flag: Boolean = (input.get(0) is Int)
if (!flag){
list = list.drop(1)
tree.left = TreeBuild(list).first
list = list.drop(1)
tree.right = TreeBuild(list).first
}
return Pair(tree, list)
}

How can I share some values among different models in elm

Update 2016-05-14
I'm sorry that I may have failed to explain my problem clearly because of my bad English. This time I've abstracted the question and demonstrated it in a counter example.
There are two sub-modules(CounterPair and CounterTriplet), and each of them uses Counter to display num filed of SharedModel and displays something else (other Counters in this example). I highlighted the Counter displaying SharedModel's num with red color.
The red Counter of CounterPair and CounterTriple should synchronized because they're representing same SharedModel. Also, shared field of Model also need to be updated when any of the red Counters updates. The code shown below have only managed to display the things as I want but the update chain is missing. How should I implement the update chain ?
You can try this code on Try Elm.
import Html exposing (div, button, text)
import Html.App as App exposing (beginnerProgram)
import Html.Events exposing (onClick)
import Html.Attributes exposing (style)
main =
beginnerProgram { model = init, view = view, update = update }
-------------------------------------
type alias SharedModel =
{ num : Int
, foo : String
}
sharedInit =
SharedModel 0 "Foo"
--------------------------------------
type alias Model =
{ shared : SharedModel
, pair : CounterPair
, triplet : CounterTriplet
}
init =
let
shared = sharedInit
in
Model shared (pairInit shared) (tripletInit shared)
view model =
div []
[ App.map Pair (pairView model.pair)
, App.map Triplet (tripletView model.triplet)
]
type Msg
= Pair PairMsg
| Triplet TripletMsg
update msg model =
case msg of
Pair sub ->
let
pair = pairUpdate sub model.pair
in
{ model | pair = pair }
Triplet sub ->
let
triplet = tripletUpdate sub model.triplet
in
{ model | triplet = triplet }
----------------------------------------
type alias CounterTriplet =
{ localFst : CounterModel
, localSnd : CounterModel
, global : CounterModel
}
tripletInit shared =
CounterTriplet (counterInit 0) (counterInit 0) (counterInit shared.num)
tripletView model =
div [ style [("background-color","lightgray"), ("margin-bottom", "1rem")] ]
[ App.map TriLocalSnd (counterView "green" model.localFst)
, App.map TriLocalSnd (counterView "green" model.localSnd)
, App.map TriGlobal (counterView "red" model.global)
]
type TripletMsg
= TriLocalFst CounterMsg
| TriLocalSnd CounterMsg
| TriGlobal CounterMsg
tripletUpdate msg model =
case msg of
TriLocalFst sub ->
let
localFst = counterUpdate sub model.localFst
in
{ model | localFst = localFst }
TriLocalSnd sub ->
let
localSnd = counterUpdate sub model.localSnd
in
{ model | localSnd = localSnd }
TriGlobal sub ->
let
global = counterUpdate sub model.global
in
{ model | global = global }
----------------------------------------------
type alias CounterPair =
{ local : CounterModel
, global : CounterModel
}
pairInit shared =
CounterPair (counterInit 0) (counterInit shared.num)
pairView model =
div [ style [("background-color","lightgray"), ("margin-bottom", "1rem")] ]
[ App.map PairLocal (counterView "green" model.local)
, App.map PairGlobal (counterView "red" model.global)
]
type PairMsg
= PairLocal CounterMsg
| PairGlobal CounterMsg
pairUpdate msg model =
case msg of
PairLocal sub ->
let
local = counterUpdate sub model.local
in
{ model | local = local }
PairGlobal sub ->
let
global = counterUpdate sub model.global
in
{ model | global = global }
---------------------------------------
type alias CounterModel =
{ num : Int
, btnClicks : Int
}
counterInit num =
CounterModel num 0
counterView color model =
div [ style [("display","inline-block"), ("margin-right", "1rem")] ]
[ button [ onClick Decrement ] [ text "-" ]
, div [ style [("color", color)]] [ text (toString model.num) ]
, button [ onClick Increment ] [ text "+" ]
, div [ ] [ text ("btn click: " ++ (toString model.btnClicks)) ]
]
type CounterMsg = Increment | Decrement
counterUpdate msg model =
case msg of
Increment ->
{ model | num = model.num + 1, btnClicks = model.btnClicks + 1 }
Decrement ->
{ model | num = model.num - 1, btnClicks = model.btnClicks + 1 }
Original Question
I'm using The Elm Architecture and the view hierarchy is like
,--------------------------------------------------------------------.
| View |
| ,------------------. ,------------------------------------------. |
| | SubView1 | | SubView2 | |
| | ,--------------. | | ,---------------. ,--------------------. | |
| | | Label(title) | | | | Label(title) | | Label(description) | | |
| | `--------------' | | `---------------' `--------------------' | |
| `------------------' `------------------------------------------' |
`--------------------------------------------------------------------'
Label is a customized <label> which can turn into <input> field when double clicked. So it is editable.
The Models is like below. The problem is I want the content of ViewModel is updated when the sub-sub label's value is edited. When I update the title label of subview1, that of subview2 should also be updated because they should "share" same Content
But the value of LabelModel is somehow stand-alone so I need to transfer the change of value all way up to ViewModel. I don't think this is a practical way of implementing what I want, because the view hierarchy may become more complicated. Besides, almost all Models have something specific (Foo, Bar, BarBar, etc. used to store states), so they need to be initialized. So Making ViewModel neat, for example type alias ViewModel = Content, is impractical, because I don't know where to get Bar and BarBar for SubViewModel1 and SubViewModel2
type alias ViewModel =
{ content : Content
, subView1 : SubViewModel1
, subView2 : SubViewModel2
}
type alias SubViewModel1 =
{ content : Content
, label : Label
, bar : Bar
}
type alias SubViewModel2 =
{ content : Content
, titleLabel : Label
, descLabel : Label
, barbar : BarBar
}
type alias Content =
{ title: String
, description : String
}
type alias LabelModle =
{ value : String
, state : Foo
}
I'm very new to Elm so this question may seems stupid, sorry for that.
Thanks for reading (and answering :) )
Elm component hierarchies work best when a single piece of data is represented only in one place. If you need the data in more than one component, you place it at the nearest common ancestor of those components.
In your concrete case, if SubViewModel1.label.value and SubViewModel2.titleLabel.value is really the same piece of data, it should live at ViewModel.
Once the data lives in that one place, you react to updates in it by reacting in the update function of the ViewModel component to a Msg of SubViewModel1 or ...2. Something like this:
module View
import SubView1
import SubView2
type alias ViewModel =
{ content : Content
, subView1 : SubView1.Model
, subView2 : SubView2.Model
, label : String -- New!
}
type Msg
= SubView1Msg SubView1.Msg
| SubView2Msg SubView2.Msg
| ...
update action model =
case action of
...
(SubView1.UpdateLabel str) as action' ->
let
(subView1', fx) =
SubView1.update action' model.subView1
in
( { model
| subView1 = subView1'
, label = str
}
, Cmd.map SubView1Action fx
)

Resources