我是一个榆树初学者,我想为我的同事提供一个榆树实用课程。
我将做一个简短的介绍榆树,然后我们将转移到实际经验教训。
第一课是一起阅读以下文件,以发现elm的基础和语法:
Basis.elm
{-
Here we will discover some elm basis and syntax.
You can hack this file and run the tests by running in your terminal:
elm reactor
And go to http://localhost:8000/
Then select Basis.elm file
______________________________________________________________________
First of all, we are going to import some testing packages.
We import the Expect package so we can use Expect package functions.
For example we will be able to call Expect.equal to check equality.
-}
import Expect
{-
Then we import Test.Runner.Html and expose TestProgram to our program.
Since we exposed TestProgram we can call this function without prefixing it with the package name.
-}
import Test.Runner.Html exposing (TestProgram)
-- Finally we import Test and expose every functions of the package
import Test exposing (..)
{-
Let's talk about functions as it's a key component of elm.
This is a function:
-}
add x y = x + y
{- ^ ^ ^^^^^
| | |
| | |--- there is no return keyword, everything is a returning expression
| |
|-|--------- first and second arguments
To call a function, you write:
myResult = myFunction myFirstArgument mySecondArgument
-}
result = add 4 5
{-
To write an anonymous function we use the following syntax
(\myFirstArgument mySecondArgument -> myFirstArgument * mySecondArgument)
so we can probably write something like that:
-}
resultComputedByAnonymousFunction = (\a b -> a + b) 4 5
{-
The signature of our add function is:
number -> number -> number
If translated in Javascript, our function looks like:
function add (x) {
return function(y) {
return x + y;
}
}
It's a function taking one number (x) returning
a function expecting another number (y) returning
a number which is the result of the operation x + y.
Functions are curried,
so we can create a new function from our add function:
-}
add3 = add 3
seven = add3 4
{-
We are going to unit test our previously written adding function.
The signature of the test function is:
String -> (() -> Expectation) -> Test
It takes a String and a function returning an Expectation and finally returns a Test.
Let's test the add function.
-}
testAddFunction = test "add function should addition the arguments" (\_ -> Expect.equal 4.12 (add 2 2.12))
-- Now let's write a dividing function
divide x y =
x / y
{-
We want a function that divide by 2, so we need to flip the arguments of the function.
So we can use a core function for that purpose:
http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Basics#flip
-}
divideBy2 = flip divide 2
-- let's compute ( 4 + 6 ) / 2
mean = divideBy2 (add 4 6)
testFunctionComposition = test "composition of divideBy2 and add functions should return the mean value" (\_ -> Expect.equal 5 mean)
{-
Finally let's find a more natural way to express what we are doing here.
We can use backward/forward function application:
http://package.elm-lang.org/packages/elm-lang/core/5.1.1/Basics#|>
-}
meanByPipe = add 4 6 |> divideBy2
testPipe = test "composition and pipe should return the same value" <|
\_ -> Expect.equal mean meanByPipe
{-
Strings are always double-quoted.
Single quote is used for Char.
The operator to append a String is ++.
-}
-- So this is a Char
myChar = 'l'
-- and those are Strings
myString = "abc"
mySecondString = "def"
testAppendOperator = test "append operator should append strings" <|
\_ -> Expect.equal "abcdef" (myString ++ mySecondString)
{-
A record is a kind of logic-less object.
This is a record:
-}
johnDoe =
{ firstName = "John"
, lastName = "Doe"
}
-- We can access a record field with the dot notation
testRecordAccessor = test "accessor .firstName should return the firstName of a record" <|
\_ -> Expect.equal "John" johnDoe.firstName
-- To get an updated copy of a record we use the pipe operator
lioDoe = { johnDoe | firstName = "Lio" }
testUpdateRecord = test "Pipe operator should 'update' the firstName of the record" <|
\_ -> Expect.equal "Lio" lioDoe.firstName
-- With the pipe operator we can change multiple fields at once
lioDa =
{ johnDoe |
firstName = "Lio",
lastName = "Da"
}
testUpdateMultipleFieldsRecord =
test "Pipe operator should 'update' multi fields" <|
\_ -> Expect.equal {firstName = "Lio", lastName = "Da"} lioDa
{-
A record is a type, so johnDoe, lioDoe and lioDa are typed { firstName: String, lastName: String }.
We can clarify typing with type annotation.
-}
janeDoe : { firstName: String, lastName: String }
janeDoe = { firstName = "Jane", lastName = "Doe" }
{-
And to simplify type usage we can alias a type.
Let's define an alias to our type { firstName: String, lastName: String }.
-}
type alias Identity =
{ firstName : String
, lastName : String
}
saraDoe : Identity
saraDoe = { firstName = "Sara", lastName = "Doe" }
-- Identity is also a record constructor
claireDoe : Identity
claireDoe = Identity "Claire" "Doe"
testRecordConstructor = test "Record constructor should return an record" <|
\_ -> Expect.equal "Claire" claireDoe.firstName
{-
Since we discovered some basic types, let's move to union type.
Union type is a flexible feature used for many things in elm.
For example, Bool is the union type of True and False.
Bool = True | False
^ ^ ^
| | |
| |-------|--- constructors
|
|------------------ the union type
With union types we can naturally express things such as an answer to a survey:
-}
type Answer = Yes | No | Other String
yes : Answer
yes = Yes
no : Answer
no = No
iDoNotUnderstand : Answer
iDoNotUnderstand = Other "i don't understand the question"
{-
We can do pattern matching with the case of instruction.
-}
testPatternMatching =
test "yes should be Yes" (\_ ->
case yes of
Yes -> Expect.pass
No -> Expect.fail "yes is not a No"
Other response -> Expect.fail "yes is not an Other"
)
main : Test.Runner.Html.TestProgram
main =
[ testAddFunction
, testAppendOperator
, testPipe
, testRecordAccessor
, testUpdateRecord
, testUpdateMultipleFieldsRecord
, testRecordConstructor
, testPatternMatching
]
|> concat
|> Test.Runner.Html.runelm-package.json
{
"version": "1.0.0",
"summary": "helpful summary of your project, less than 80 characters",
"repository": "https://github.com/user/project.git",
"license": "BSD3",
"source-directories": [
"."
],
"exposed-modules": [],
"dependencies": {
"elm-community/elm-test": "4.1.1 <= v < 5.0.0",
"elm-community/html-test-runner": "1.0.3 <= v < 2.0.0",
"elm-lang/core": "5.1.1 <= v < 6.0.0",
"elm-lang/html": "2.0.0 <= v < 3.0.0"
},
"elm-version": "0.18.0 <= v < 0.19.0"
}然后我们将编写一些单元测试来发现列表和元组,编写一个简单的应用程序来发现Elm体系结构,最后用命令构建一个更复杂的应用程序。
您能回顾一下Basis.elm文件来改进这一课吗?
发布于 2017-09-08 21:35:44
我不知道有一种榆树语--你的帖子把我介绍给了它。
resultComputedByAnonymousFunction = (\a b -> a + b) 4 5在白板上阅读时,人们必须能够发音。显然,这里正确的发音会涉及到"lambda a b“。我觉得这一解释有点惊讶。我认为( \ a b ...也会同样工作;这与为换行符编写\n完全不同。我不知道您的听众是否对希腊字母过敏,但是您可能需要稍微扩展匿名函数部分。
meanByPipe = add 4 6 |> divideBy2这很好,你是一次只介绍一件事,在这种情况下,一个新的操作符。但在下一行中,您将<|弹到我们身上。您可能需要定义两次testPipe,显示带有和不带<|的等效函数。
我建议用这样的措辞:单引号用于单个字符。
对于johnDoe /别名/ main,我认为您认为在行开始时使用逗号可以提高可读性或最小化差异。我不同意你的观点(比如lioDa),但那没什么。Python让我很不爽,因为它允许用逗号尾随,所以所有的行都是相同格式的。
在yes = Yes之前,您可以明确指出它们是两个不同的标识符。
main给我的印象是有点脆弱,因此很容易添加testFoo,而忽略了将其包括在列表中。如果有一些内省方法来获取标识符列表,我鼓励您在这里使用它。
总的来说,你的语言和说明文都很清楚,适合刚开始的听众。
https://codereview.stackexchange.com/questions/174377
复制相似问题