how to divide a large Elm program into smaller components - elm

I'd like to separate the View and Update parts of a program into separate source files, but then, how do I share the Message and Subscriptions declarations ?

Think twice before splitting your modules, premature code splitting might be harmful to your project.
Here is an example of the project structure I use in my Elm apps.
Messages
Html.App.map allows us to tag a message for child component, so we could pass it to Components.Counter.Update.update function, when keyboard subscription emits a message.
module App.View exposing (..)
import Html.App
import Html exposing (text, div, Html)
import App.Model exposing (Model)
import App.Messages exposing (..)
import Components.Counter.View
view : Model -> Html Msg
view model =
div []
[ Html.App.map Counter (Components.Counter.View.view model.counter) ]
Subscriptions
To tag a message from a subscription, we have to use Platform.Sub.map
Please see an example of subscription passing in src/App/Subscriptions.elm
module App.Subscriptions exposing (..)
import App.Model exposing (Model)
import App.Messages exposing (..)
import Components.Counter.Subscriptions
subscriptions : Model -> Sub Msg
subscriptions model =
let
counterSub =
Components.Counter.Subscriptions.subscriptions model.counter
in
Sub.batch [ Sub.map Counter counterSub ]
File structure
Main.elm -- Entry point, where App is initialized
Utils.elm -- Utilities
App/
Messages.elm
Model.elm
Subscriptions.elm
Update.elm
View.elm
Components/
StatefulComponent/
Messages.elm
Model.elm
Subscriptions.elm
Update.elm
View.elm
StatefulComponentWithCommands/
Commands.elm -- Functions, which return Cmd msg
Messages.elm
Model.elm
Subscriptions.elm
Update.elm
View.elm
StatelessComponent/
View.elm

An Elm program can be progressively split into several files as follows :
extract message declarations and associated types into Messages.elm
Add import Messages exposing (..) in Main.elm.
Compile it and test.
extract model declaration and initialisation into Models.elm.
Add import Messages exposing (..) in Models.elm.
Add import Models exposing (..) in Main.elm
Compile it and test
extract the view function into View.elm.
Add import Messages exposing (..) in View.elm.
Add import Models exposing (..) in View.elm
Add import View exposing (..) in Main.elm
Compile it and test
A the end, Main.elm still has the subscriptions and update parts. They can be extracted similarly.
See example at github.com/sporto/elm-tutorial-app/tree/master/src, with corresponding tutorial at elm-tutorial.org/en/05-resources/01-intro.html.

Related

Is it expected behaviour for scrapy nested loaders to create duplicate values from base item?

I'm using Scrapy and want to collate values from multiple related pages into a single item (e.g. a user profile spread across a number of pages -> user item). So I'm creating an itemloader and then passing the item after scraping to the parser for the next request for it to add the values from the next page. The problem I'm having is that as soon as I nest a loader, all the values in the base item are duplicated.
from scrapy.loader import ItemLoader
from scrapy.selector import Selector
l = ItemLoader(item={'k':'v'},response=(''), selector=Selector(text=''))
nl = l.nested_css('.test')
print(l.load_item())
>>> {'k': ['v', 'v']}
So the workaround is to not use nested loaders, but am I doing something wrong, or is this a defect?

Confused about bs4 imorting methods and their effect on attributes

I know versions of this question were asked in the past, but I'm still confused and would like to settle my doubts once and for all, if possible.
If I use
from bs4 import BeautifulSoup
my soup assignment is going to be
soup = BeautifulSoup(html, "lxml")
If I do the importing thus:
from bs4 import BeautifulSoup as bs4
my soup assignment is
soup = bs4(html, "lxml")
Finally, if I import using:
import bs4
my soup assignment is
soup = bs4.BeautifulSoup(html, "lxml")
Let's use a simple html and code:
html = """
Some Document
"""
link = soup.select('a:contains(Document)')
Next, the main question:
type(link[0])
The output - in all three import cases - is:
bs4.element.Tag
But if I ask:
isinstance(link[0],bs4.element.Tag)
In the third case, I get True, but in the first two cases, I get
AttributeError: type object 'BeautifulSoup' has no attribute 'element'
Since the select() and find_all() methods frequently deliver bothTag or NavigableString results, I need to determine which is which using, for example, isinstance(). So in those cases, do I have to use the third import method? Why is there a difference in the first place?
This is a naming game you are doing. Lets go ahead and state that class bs4.element.Tag is the class of element instances. Think of that as the absolute location of the Tag class in bs4. bs4.element represents the nested modules with Tag (which is found under the element module) being the class in which the elements are instances of. When displaying the class info of those elements, it will always show bs4.element.Tag.
Now, with all of that said, you can access the BeautifulSoup object in different ways. And none of this changes the fact that element tags are of type bs4.element.Tag. When you import bs4:
import bs4
bs4.BeautifulSoup()
This imports the module under the module's default name bs4. And then you can access BeautifulSoup in that module with the dot notation as BeautifulSoup is a member of that module. But locally bs4 is just a variable that references the bs4 module.
When you import as:
from bs4 import BeautifulSoup as bs4
bs4 does not mean the same thing as the first example. In the first example we imported the entire module under its default name (bs4), but here we instead import the BeautifulSoup class and rename it locally as bs4. Regardless of what we call it locally, it is still a class at bs4.BeautifulSoup, where bs4 is the module name. Locally though (local to this file), we created a variable reference to the BeautifulSoup class with a name that happens to be the same as the module.
So, when you use select to return elements, they are of the type bs4.element.Tag. This is true regardless of what your local variables happen to be named. This is internally how they are known.
So, when comparing instance, it is important to know, the variable name is not important, what is important is what the variable is referencing. In the third example, import bs4 causes bs4 to reference the bs4 module; therefore, Tag can be accessed at bs4.element.Tag. But in the case where you use from bs4 import BeautifulSoup as bs4, bs4 no longer references the bs4 module, it references the BeautifulSoup class which has no attributes called element with the attribute Tag as it is not a module but a class.
The local name is just how your current file is referencing the object it refers to.
So in your failing cases, you would need to import the Tag reference to a variable you can provide to instance:
>>> from bs4 import BeautifulSoup
>>> from bs4.element import Tag
>>> soup = bs4.BeautifulSoup('<div>Test<span>test</span><span>test2</span></div>')
>>> isinstance(soup.find('div'), Tag)
True
Tag here is just a name, but it references bs4.element.Tag, so it works.
We could call it anything and it will still work as long as it references the correct object:
>>> from bs4 import BeautifulSoup
>>> from bs4.element import Tag as Apple
>>> soup = bs4.BeautifulSoup('<div>Test<span>test</span><span>test2</span></div>')
>>> isinstance(soup.find('div'), Apple)
True
Hopefully that makes more sense :).
EDIT: Just a tip, but bs4 makes some references to things like NavigableString and Tag available in the top level module, so you don't have to reach all the way down to bs4.element to get a proper reference, you can simply do:
from bs4 import Tag, NavigableString
Again, this alternative reference of bs4.Tag is just a variable named Tag in the bs4 module that refers to the actual bs4.element.Tag class. You can use that, and it will still refer to the same class. It is just used locally in the bs4 module to reference the Tag class in element.

Elm 0.18 how to replace Html.App

I am new to elm and I'm working on the ElmBridge tutorial. I understand Html.App collapsed into Html and any Html.App imports need to refer to Html.program instead. I replaced Html.App with Html.program but the module is not importing.
When I run elm-make I get a syntax problem saying it's looking for an upper case name? Screen shot of my Main.elm and error below.
Main.elm
Error message
You don't import Html.program, only Html.
import Html
Then you use it by referencing as you already have,
main = Html.program {...}
-- or...
main = Html.beginnerProgram {...}
There is no "Program" module. It's in Html itself.
Just do import Html and then you can use Html.program

Typescript, Requirejs, import statement and aliases

With Java, import is really easy and clear.
You import with the following statement :
import fr.domain.MyUtils;
Then you can use it like this:
MyUtils.myStaticMethod();
You need to namespace MyUtils only if there are two in the same file.
With Typescript AMD and requirejs it seems to be more complicated.
Here the import statement:
import u = require('fr/domain/MyUtils');
And the way to use it:
u.fr.domain.MyUtils.myStaticMethod();
Quite verbose...
The only way I found so fare to use an alias was to double the import statement:
import u = require('fr/domain/MyUtils');
import MyUtils = u.fr.domain.MyUtils;
After doing that you can write this in a module:
MyUtils.myStaticMethod();
It's cleaner but Eclipse TS plugin get completely lost with this and auto completion become erratic. In Visual Studio auto completion is OK but "F12 Go to definition" has to be done twice which is annoying.
Is there a better way to do this ? Or should we just keep namespaces as short as we can ?
You’re doing it wrong.
Your 'fr/domain/MyUtils' module should be exporting only whatever is supposed to be MyUtils. i.e. it should look like this:
export function myStaticMethod() { /* ...code... */ }
It should not be exporting some global namespace object, and it should not be adding anything to some global namespace object that you get from somewhere else. The natural placement of module files in directories is the way you create “namespaces” when you work with external modules.
If you do it correctly then your consumer looks like this:
import MyUtils = require('fr/domain/MyUtils');
MyUtils.myStaticMethod();
or, even more properly using ES module syntax:
import { myStaticMethod } from 'fr/domain/MyUtils';
myStaticMethod();

Place a text content in the middle of a container

I am using collage, so that the elements will be placed in the middle of the box.
import Graphics.Element exposing (show)
import Graphics.Collage exposing (collage)
textBox =
show "hello world"
main =
collage 1000 1000 [textBox]
But there is a typemismatch error at the last line as,
Graphics.Element.Element
Graphics.Collage.Form
Since, show function returns Element whereas collage accepts only Form. What other function can i use, to position the text content in the middle of the collage?
You can convert an Element to a Form with Graphics.Collage.toForm
toForm : Element -> Form
http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Graphics-Collage#toForm
Your program simply becomes
main = collage 1000 1000 [toForm textBox]
grumpyjames' answer is right about converting Element to Form to put them on a collage. I just want to point out that you don't need to use a collage to put an Element in the center. The Graphics.Element package has a container function that will serve a similar purpose to collage, but with Element instead of Form. So you could also do:
import Graphics.Element exposing (..)
main =
container 1000 1000 middle (show "Hello, World!")