Skip to content
This repository
tree: 953f9d129b
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

file 912 lines (664 sloc) 30.067 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911
Getting started
===============

Your first Opa program:

* Get Opa from [its web-page](http://opalang.org/).
* In `hello.opa` write:

    Server.start(Server.http,
      { title: "Chat"
      , page: function() { <>Hello web</> }
      }
    )

* Compile (yes, Opa is a *compiled* language) with: `opa hello.opa`
* Run with: `./hello.exe`
* (You can also combine the two above steps with: `opa --parser js-like hello.opa --`)
* Test by opening [http://localhost:8080](http://localhost:8080) in your browser.

The language
============

Comments
--------

Opa supports both single and multi-line comments.

    // This is a single line comment. It goes until the end of the line

    /* this is a
       multi-line comment,
       which must be closed with: */

Type declarations
-----------------

Opa features almost complete type-inference so often, especially at the prototyping phase, you can write your programs without explicitly defining any types and they will be inferred by the compiler. However, you'll learn to appreciate the value of defining explicit types for important notions in your program: it greatly improves readability of your programs (serving as a documentation of sorts) and also allows Opa to produce much more readable and accurate error information.

     // type abbreviation
    type age = int

     // functional types
     /* meaning: a function taking two arguments: the first one is an int, the
        second one is a string. It returns an integer */
    type binary_fun = int, string -> int

     // record types
    type person = { string name, int age }

     // parametrized (polymorphic) types
     /* meaning: a binary operator of a parametric type */
    type operator('ty) = 'ty, 'ty -> 'ty

     // variant (sum) types
    type boolean = {true} or {false}
     // equivalent to:
     /* i.e. we can omit the type if it's void, in which case the record
        field just acts as a label (carries no information, except for its presence). */
    type boolean2 = {void true} or {void false}

     // parametrized variant type
     type option('a) = {'a some} or {none}

     // recursive type
    type list('a) = {nil} or {'a hd, list('a) tl}
    type bin_tree('a) = {'a leaf} or {tree('a) left, tree('b) right}

//[TODO]: Add module types and functor types

Expressions
-----------

### Functions

#### Function declarations

     // function declaration
    function int incr(int x)
    {
      x + 1
    }

     // type annotations for arguments and results can be omitted
    function incr_alt(x) { x + 1 }

     // function with local bindings and using tuples for arguments
    function distance((x1, y1), (x2, y2)) {
      dx = x1 - x2
      dy = y1 - y2
      Math.sqrt_f(dx*dx + dy*dy)
    }

     // anonymous function
    function(x) { x + 1 }

     // mutually recursive functions; 'recursive' and 'and' only
     // needed for *local* functions (not needed at top-level)
    recursive function odd(x) { if (x == 1) true else even(x-1) }
    and function even(x) { if (x == 0) true else odd(x-1) }

//[TODO]: Add recursive functions
//[TODO]: Add partial application

#### Function/module modifiers

The following modifiers can alert the declaration of the function in some way.

     // Visibility modifiers:
    private function priv_fun() {...} // function is invisible outside of its module
    public function pub_fun() {...} // function is visible from other modules

     // Distribution modifiers:
    server function server_fun() {...} // function will be on the server
    client function client_fun() {...} // function will be on the client

     // Security modifiers:
    exposed function exposed_fun() {...} // marks an entry-point (function can be called from the client)
    protected function protected_fun() {...} // function should not be exposed to the client

### Strings

Strings are primitive types in Opa.

String literals are constructed using double quotes `"..."`. Please note that single quotes `'...'` are **not allowed** for strings in Opa.

    string s = "This is a string"

String concatenation. See also [here](/refcard/Standard-library/General-data-types/string) for more about functions for string manipulation.

    string hello = "Hello" ^ " world"

A particular feature of Opa are *inserts*, which you'll use a lot. They allow to insert values into a string using curly braces.

     // hence:
    "{x} + {y} = {x+y}"
     // is equivalent to:
    x ^ " + " ^ y ^ " = " ^ (x + y)

You'll see that Opa also features [HTML inserts](/refcard/The-language/Expressions/XHTML).

### XHTML

XHTML is a [defined](/type/stdlib.core.xhtml/xhtml) not a primitive type in Opa, but there is a built-in syntax support for constructing XHTML values.

Here we only present an overview of the syntax used for XHTML; you'll find more info about functions for operating on this data-type [here](/refcard/Standard-library/Web-features/XHTML).

    type xhtml = ...
     // XHTML is a data-type with built-in syntax
    xhtml span = <span class="test">Hello XHTML</span>
     // closing tag is optional, so are the quotes around literal attributes
    span2 = <span class=test>Hello XHTML</>

Inserts work in XHTML too (both for tags & attributes)

    function mk_span(class, body) {
      <span class="{class}">
        {body}
      </>
    }

#### DOM manipulations

DOM (Document Object Model) is a representation of X(HT)ML. You will often interact with the DOM to dynamically change the content of the page.

You will find more about functions to manipulate the DOM [here](/refcard/Standard-library/Web-features/DOM). Below we just present few syntactical goodies that make it easier to make some most common transformations.

     // manipulating (replacing/prepending/appending) DOM content for a given ID
    #toto = <h1>Replace</>
    #toto += <h1>Prepend</>
    #toto =+ <h1>Append</>

     // manipulating DOM selection
    dom_pointer = Dom.select_children(#toto)
    *dom_pointer += <h2>before</>

// manipulating CSS
// #toto css = {color : blue}
// [WARNING] manipulating CSS above; syntax doesn't work as of Dec 1, 12:00

### Records

While working with Opa you'll use records *a lot*. Therefore it's probably a good idea to get familiar with their syntax right away.

     // full record construction
    john = { name: "John Smith", age: 31}
     // accessing record fields
    john_string = "Name: {john.name}, Age: {john.age}"

     // tilde-shortcut
    name = ...
    age = ...
     // '~field' within the record abbreviates 'field: field'
    person = { ~name, ~age }
     // one can even also abbreviate all fields by putting tilde in front of the record
    person2 = ~{ name, age }
     // both 'person' and 'person2' are equivalent to
    person3 = { name: name, age: age }

     // record extension
     // meaning: take record 'john' and replace its 'age' field with the given value
    older_john = { john with age: john.age + 1 }

### Pattern matching

Pattern matching is used to analyze values that may take several variants.

     // pattern matching on boolean values
    bool x;
    match (x) {
      case {true}: ...
      case {false}: ...
    }

     // pattern matching on a record representing an URL
    match (url) {
       // record field path is an empty list
       // '...' indicates there may be more fields in the record
      case {path:[] ...}: show_root()
       // any path, bound to 'path' identifier
      case {path:path ...}: show_at(path)
       // equivalent to the above case
      case {~path ...}: show_at(path)
       // default case
      default: show_root()
    }

//### Parsing
//[TODO]

Database
--------

In Opa the database is tightly integrated in the language. At the moment the compiler supports 2 DB backends:

- db3: Opa's internal database
- MongoDB: [http://www.mongodb.org/](http://www.mongodb.org/)

One can choose the back-end with the `--database {mongo/db3}` compiler switch. Combining two different backends in one program is possible.

### Simple types

#### Declaring

Declare a collection of database values called `person`

    database person {
       // it contains an integer value 'age'
      int /age
       // a float value 'weight'
      float /weight
       // and a string value 'name'
      string /name
    }

#### Reading

The primitive values can be read with `/db_name/field_name` notation

    function string present_person() {
      "Hey, my name is {/person/name}, I'm {/person/age} years old and I weight {/person/weight} kg."
    }

#### Modifying

You can simply set a value:

    /person/age <- 37
    /person/weight <- 76.5
    /person/name <- "John Doe"

You can also increment or decrement integer values.

    /person/age++
    /person/age -= 20

### Records

#### Declaring

Database declaration can also include records

    type person = { int age, string name }

    database people {
      person /me
    }

#### Reading

We can read the whole record

    person myself = /people/me

or just a chosen field

    int my_age = /people/me/age

#### Modifying

Similarly we can update the whole record

    /people/me <- { age: 27, name: "Unknown" }

or just some of its fields:

    /people/me/name <- "John Doe"
    /people/me/age++

### Lists

Lists are in fact just records, but there are few special syntactical goodies for operating on them:

    database cities {
      list(string) /capitals
    }

Overwrite an entire list

    /cities/capitals <- ["Amsterdam", "New York City", "Paris"]

Remove first and last element of a list

     // first one
    string city1 = /cities/capitals pop // city1 == "Amsterdam"
     // last one
    string city2 = /cities/capitals shift // city2 == "Paris"

Adding elements:

     // Append one element
    /cities/capitals <+ "Tokyo"
     // Append several elements
    /cities/capitals <++ ["Mumbai", "Delhi", "Shanghai"]

After all those operations we have:

    /cities/capitals == ["New York City", "Tokyo", "Mumbai", "Delhi", "Shanghai"]

### Sets and Maps

Sets and maps are very powerful concepts allowing to better organize and query data.

#### Declaring

Let us begin with declaring a *set* of persons

    type user_status = {regular} or {premium} or {admin}
    type user = { int id, string name, int age, user_status status }

    database users {
      user /all[{id}]

       // the status field is user-defined so we need to specify the default value
      /all[_]/status = { regular }
       // or to indicate that we will only manipulate full-records
      // /all[_] full
    }

The `[{id}]` after the path `/all` indicates that we are declaring a *set* and not a single `user` value and the `{id}` value indicates that `id` field will be the *primary key*.

To illustrate maps we will just use a simple abstract example with a map from `int`s to `string`s.

    database map {
      intmap(string) /m
    }

#### Querying

We can fetch a single value from a set with a given key:

    key = {id: 123}
    user user1 = /users/all[key]

{block}[WARNING]

Note that in Opa 0.9.0 abbreviating the above with

    user user1 = /users/all[{id: 123}]

is not possible. This will be fixed in the subsequent version.
{block}

Similarly for maps:

    string v = /map/m[123]

For an overview of querying syntax and options we refer to the [relevant chapter](/manual/Hello--database/Sets-and-Maps) in the manual. Here we will just provide some examples.

Examples for sets:

// TODO First example should just return a single value

    user /users/all[id == 123] // accessing single entry by its primary key
    dbset(user, _) john_does = /users/all[name == "John Doe"] // return a set of values
    dbset(user, _) underages = /users/all[age < 18]
    dbset(user, _) non_admins = /users/all[status in [{regular}, {premium}]]
    dbset(user, _) /users/all[age >= 18 and status == {admin}]
    dbset(user, _) /users/all[not status == {admin}]
     // showing second 50 results for users that are below 18 or above 62,
     // sorted by age (ascending) and then id (descending)
    dbset(user, _) users1 = /users/all[age <= 18 or age >= 62; skip 50; limit 50; order +age, -id]

Examples for maps:

    string /map/m[123] // unique map association
    intmap(string) /map/m[< 123 and > 456] // a sub-map for keys below 123 and above 456

// TODO: Add updating; one example doesn't work for now...
//#### Updating

//Complete, single-value update:

// /users/all[id == 123] <- {name: "John Doe", age: 32, status: {regular}}

//Partial, many-values update:

// /users/all[age < 32] <- {age++}

Structuring programs
--------------------

### Packages

     // declaring file's package
    package mlstate.tutorials.refcard

     // importing other packages
    import stdlib.web.mail
     // more than one package at once
    import stdlib.widgets.{button, dateprinter}
     // all sub-packages
    import stdlib.apis.facebook.*

### Modules

     // declaring a module
    module ModuleA {
      function fooh() {
        ...
      }
    }

     // calling functions from another module
    module ModuleB {
      function bar() {
        ModuleA.fooh();
        ...
      }
    }

     // nested modules
    module OuterModule {
      private module InnerModule {
        ...
      }
    }

//[TODO]: Add functors...

Debugging Opa
-------------

[Log](/module/stdlib.core/Log) messages

     // Add logs with Log.debug/info/notice/warning/error/fatal
    Log.info("event_type", "debug msg, x={x}, v={v}")
     /* note that the log will either appear in the browser (use Development Console to see it)
      * or in the terminal where you executed your server, depending on where the code with
      * the debug command is executed
      */

The compile cycle is rather long and you don't want to use it to tweak with resources (CSS) etc. Instead you can:

* compile your app in development mode (without `--compile-release` switch);
* run it with the `-d` switch (or `--debug-editable-css` or similar);
* `opa-debug` directory will be created with app resources;
* edit them and see the changes in your app immediately;
* *Remember*: when you're finished you still need to update app resources in their respective directory! (so that changes are kept when you recompile then app)

Standard library
================

//General structure
//~~~~~~~~~~~~~~~~~

//* +stdlib.core+: core packages.
//* +stdlib.widgets+: web widgets (stateless)
//* +stdlib.components+: web components (_i.e._ stateful widgets)
//* +stdlib.api+: APIs to external services, (_ex._ Facebook, Twitter, ...)

//Important packages
//------------------

// parser?

//* <<datatypes,*General data-types*>>: <<bool,bool>>, <<float,float>> <<int,int>>, <<string,string>>, <<date,date>>, <<option,option>>.
//* <<containers,*Containers*>>: <<list,list>>, <<map,map>>, <<set,set>>.
//* <<webfeatures,*Web features*>>: <<Server,Server>>, <<xhtml,XHTML>>, <<css,CSS>>, <<dom,DOM>>, <<uri,URLs>>, <<resource,Resource>>, http://ap.opalang.org/module/stdlib.core.color/Color[color], /package/stdlib.web.canvas[canvas], /package/stdlib.web.mail[mail]
//* <<other,*Other*>>: <<markdown,Markdown>>, <<parsing,Parsing>>
//* <<external_apis, *External APIs*>>: /package/stdlib.apis.couchdb[CouchDB], /package/stdlib.apis.facebook[Facebook], /package/stdlib.apis.github[GitHub], /package/stdlib.apis.gmaps[Google Maps], /package/stdlib.apis.irc[IRC], /package/stdlib.apis.mongo[MongoDB], /package/stdlib.apis.oauth[OAuth], /package/stdlib.apis.rdf[RDF], /package/stdlib.apis.recaptcha[ReCaptcha], /package/stdlib.apis.twitter[Twitter], /package/stdlib.apis.worldweather[World Weather Online]

General data types
------------------

### bool

     // type definition
    type bool = {false} or {true}

    // conditionals
    if (b) { ... } else { ... }
     // pattern matching
    match (b) {
      case {true}: ...
      case {false}: ...
    }

[#See the full API#](/module/stdlib.core/Bool)

### float

     // literal -- the decimal dot makes it a float, not an int
    float f = 10.
     // operations
    distance = Math.sqrt_f(dx*dx + dy*dy)
     // conversion to int
    int x = Float.to_int(f)
     // conversion from int
    float f = Float.of_int(17)
     // conversion to string
    string s = Float.to_string(3.14159)
       // or simply with inserts
    string s = "Value of f is: {f}"
     // conversion from string
    option(float) f = Parser.try_parse(Rule.float, "3.14159")

[#See the full API#](/module/stdlib.core/Float)

### int

     // literal
    int i = 10
     // conversion to float
    float f = Int.to_float(i)
     // conversion from float
    int i = Int.of_float(3.14159)
     // conversion to string
    string s = Int.to_string(42)
       // or simply with inserts
    string s = "Value of i is: {i}"
     // conversion from a string
    option(int) i = Parser.try_parse(Rule.integer, "42")

[#See the full API#](/module/stdlib.core/Int)

### string

     // literal
    string s = "This is a string"
     // concatenation
    s = s1 ^ s2
       // or with inserts
    s = "Hey, {name}, nice to meet you!"
     // length
    String.length("Hello") == 5
     // n'th character
    String.get(1, "Hello") == "e"
     // dividing at a separator
    String.explode(",", "1,2,3") == ["1", "2", "3"]
     // flattening a list of string
    String.flatten(["1", "2", "3"]) == "123"

See also: [String.capitalize](/value/stdlib.core/String/capitalize), [String.get_prefix](/value/stdlib.core/String/get_prefix), [String.get_suffix](/value/stdlib.core/String/get_suffix), [String.has_prefix](/value/stdlib.core/String/has_prefix), [String.has_suffix](/value/stdlib.core/String/has_suffix), [String.init](/value/stdlib.core/String/init), [String.lowercase](/value/stdlib.core/String/lowercase), [String.print_list](/value/stdlib.core/String/print_list), [String.replace](/value/stdlib.core/String/replace), [String.reverse](/value/stdlib.core/String/reverse), [String.substring](/value/stdlib.core/String/substring), [String.trim](/value/stdlib.core/String/trim), [String.uppercase](/value/stdlib.core/String/uppercase)

[#See the full API#](/module/stdlib.core/String)

//### date
//[TODO]

### option

Optional value of any type -- a type-safe approach to null-values.

     // type definition
    type option('a) = {'a some} or {none}
     // construction
    opt1 = none
    option(string) opt2 = some("Hello")
     // inspecting values (pattern matching)
    match (opt) {
      case {none}: ...
      case {some: value}: ...
    }
     // default value; opt of type option(string)
    string s = opt ? "default"

See also: [Option.map](/value/stdlib.core/Option/map), [Option.switch](/value/stdlib.core/Option/switch)

[#See the full API#](/module/stdlib.core/Option)

Containers
----------

### list

Lists in Opa are homogeneous, i.e. all elements must have the same type. It's the simplest container and you will use lists _a lot_.

     // definition
    type list('a) = {nil} or {'a hd, list('a) tl}

     // construction
    list(int) few_primes = [2, 3, 5, 7]
     // concatenation
    l12 = l1 ++ l2
     // adding element 'elt' at the beginning of list 'l'
    {hd: elt, tl: l}
    [elt | l] // equivalent
    List.cons(elt, l) // also equivalent
     // list length
    List.length([2, 4]) == 2

     // pattern matching
    match (l) {
      case []: ... // list empty
      case [hd | tl]: ... // first element 'hd' followed by list 'tl'
    }

     // modifying all elements (increasing by 1)
    List.map(function(elt) { elt + 1 }, [2, 4]) == [3, 5]
     // aggregating elements (multiplying all elements)
    List.fold_left(function(acc, elt) { acc*elt }, 1, [2, 4]) == 8
     // sorting a list
    List.sort([4, 5, 2]) == [2, 4, 5]
     // calling a function for every element of the list
    List.iter(function(elt) { Log.info("ELT", "{elt}") }, [2, 4])
     // check if element belongs to the list
    List.mem(3, [6, 3]) == true
     // converting to string
    List.to_string([1, 3, 5]) = "[1, 3, 5]"

See also: [List.assoc](/value/stdlib.core/List/assoc), [List.exists](/value/stdlib.core/List/exists), [List.filter](/value/stdlib.core/List/filter), [List.find](/value/stdlib.core/List/find), [List.flatten](/value/stdlib.core/List/flatten), [List.get](/value/stdlib.core/List/get), [List.init](/value/stdlib.core/List/init), [/value/stdlib.core/List/nth](List.nth), [List.remove](/value/stdlib.core/List/remove), [List.rev](/value/stdlib.core/List/rev).

[#See the full API#](/module/stdlib.core/List)

### map

Maps (dictionaries, hashmaps) are *immutable* structures mapping keys to values. They are comparable to hash-tables in other languages.

     // types
    type map('key, 'val) // map from 'key's to 'val'ues with default ordering
    type stringmap('t) // map from strings to type 't
    type intmap('t) // map from ints to type 't

     // construction
    stringmap(int) map = Map.singleton("ten", 10) // a map from strings to ints
    intmap({string name, int age}) s = Map.empty // a map from ints to records
     // conversion from list
    stringmap(int) map = Map.From.assoc_list([("one", 1), ("six", 6)])
     // conversion to list
    Map.To.assoc_list(map) == [("one", 1), ("six", 6)]

     // extending maps
    new_map = Map.add("six", 6, map)
     // checking if an element belongs to a map
    if (Map.mem("seven", map)) { ... } else { ... }
     // getting an element from a map
    option(int) v = Map.get("seven", map)

Iterators: `fold`, `map`, `iter`, `filter` -- similar to [lists](/refcard/Standard-library/Containers/list).

[#See the full API#](/package/stdlib.core.map)

//[TODO] add See also

### set

Sets are containers holding and allowing to manipulate a number of elements of the same type.

     // types
    type set('elem) // set with elements of type 'elem
    type intset // set of integers
    type stringset // set of strings

     // construction
    stringset names = Set.singleton("John Smith")
     // from list
    Set.From.list([1, 4, 7])
     // to list
    Set.To.list(names) == ["John Smith"]

     // extending sets
    Set.add("Joe", names)
     // checking if an element belongs to a set
    if (Set.mem("Dave", set)) { ... } else { ... }
     // getting an element from a set
    option(int) v = Set.get("Alice")

Iterators: `fold`, `map`, `iter` -- similar to [lists](/refcard/Standard-library/Containers/list) and [maps](/refcard/Standard-library/Containers/map)

[#See the full API#](/package/stdlib.core.set)
//[TODO] add See also

Web features
------------

### Server

This module allows one to declare Servers, i.e. entry-points of Opa programs. One declares a server with `Server.start` that gets two parameters: configuration and a handler defining how to map URIs to resources.

     // Start a server defined by 'handler' with configuration 'conf'
    Server.start(Server.conf conf, Server.handler handler)

     /* Configurations (1st argument of Server.start) */
    Server.http // default HTTP config (on port 8080)
    Server.https // default HTTPS config (on port 8080)
    {port: 80, netmask:0.0.0.0, encryption: {no_encryption}, name:"my server"} // custom server

     /* Handlers (2nd argument of Server.start) */
    function mypage() {
      <>Hello web!</>
    }
     // single page server (URI ignored, always the same page)
    { page: mypage
    , title: "My app"}

     // multi-page server, function from URI (string) to a resource
    dispatcher = parser
    | "/" -> Resource.page("Hello", <>Hello web</>)
    | "/_rest_/" .* -> Resource.page(...)
    | .* -> Resource.page(...)
    { custom: dispatcher }

     // multi-page server, function from URI (structured) to a resource
     // + possibility to add a filter to decide which URIs to handle
    function start(url) {
      match (url) {
        case {path:[] ... }: ...
        case {~path ...}: ...
      }
    }
    { dispatch: start }

     // simple server for *serving* a bundle of resources
    { resources: @static_resource_directory("resources") }

     // no request handling but registering a list of custom resources (JS/CSS)
    { register: {css: ["resources/css/style.css", "resources/js/myjs.js"]} }

     // One can also use a list of servers.
     // For instance bundle + custom resources + dispatcher).
     // Helpful for building servers in a structured way
     // Servers are tried in the given order and the first successful will serve the request
    Server.start(
      Server.http,
      [ {resources: @static_resource_directory("resources")} // serve custom resources
      , {register: {css: ["resources/css.css"]}} // use custom CSS
      , {title: "Chat", page:start } // serve the one-page app!
      ]
    )

[#See the full API#](/module/stdlib.core.web.server/Server)

### XHTML

In Opa XHTML is a data-type, with special syntax support.

See [syntax introduction](/refcard/The-language/Expressions/XHTML) for more info.

    page = <span class="hello">Hello web</span>

     // name in the closing tag is optional
     // so are the quotes for attributes
    page = <span class=hello>Hello web</> // equivalent

     // you can pass XHTML as arguments to functions
     // and use it with inserts
    function block(title, content) {
      <div class=block>
        <span class="{class}">
        </span>
    }

[#See the full API#](/module/stdlib.core.xhtml/Xhtml)

### CSS

     // just like XHTML, CSS is a data-type in Opa
    red_style = css { color: red }
    span = <span style={red_style} />

     // one can use inserts inside css
    function div(width, height, content) {
      <div style={ css { height: {height}px; width: {width}px }}>
        {content}
      </>
    }

     // a top-level css section defines application-wide css
    css = css
      .body {
        background: red
      }

     // one can also register external css (from a separate project file)
    Server.start(Server.http,
      [ {register: {css: ["resources/css/style.css"]}}
      , ...
      ])

It's very easy to use [Bootstrap](http://twitter.github.com/bootstrap) in Opa. Check [this page](http://bootstrap.opalang.org) for more info.

See [syntax introduction](/refcard/The-language/Expressions/XHTML) for more info.

[#See the full API#](/module/stdlib.core.xhtml/Css)

### DOM

See [syntax introduction](/refcard/The-language/Expressions/XHTML) for some introduction to DOM manipulation.

     // get a fresh ID
    Dom.fresh_id()

     // DOM selections
    #test // element with ID "test"
    #{test} // element with ID equal to (string) variable 'test'
    Dom.select_all() // complete document
    Dom.select_class(class) // all elements with class 'class'
     // and much more...

     // DOM effects
    Dom.transition(#some_id, Dom.Effect.fade_out())
    Dom.transition(dom_selection, Dom.Effect.slide_in())

For more effects and ways of applying them see the: /module/stdlib.core.xhtml/Dom/Effect[Dom.Effect] module.

[#See the full API#](/module/stdlib.core.xhtml/Dom)

### URLs

     // URI definition:
    type Uri.uri = Uri.absolute or Uri.relative or Uri.mailto
     // where Uri.relative is:
    type Uri.relative =
       { list(string) path
       , option(string) fragment
       , list((string, string)) query
       , bool is_directory
       , bool is_from_root
       }

     // When constructing a server you can dispatch pages by their relative URIs:
    function dispatch(url) {
      match (url) {
        case {path:[] ... }: ...
        case {path:["_rest_" | _] ...}: ...
        default: ...
      }
    }
    Server.start(Server.http, { dispatch: start })

[#See the full API#](/module/stdlib.core.web.core/Uri)

### Resource

Resources are objects that can be served by the server to the clients, such as web pages, images, scripts, stylesheets etc.

     // Constructing resources
    Resource.page("Page title", <span>Page body</>) // HTML page
    Resource.styled_page(title,
      ["resources/style.css"], body) // HTML page + custom CSS
    Resource.image({png:
      @static_source_content("./resources/index.jpg")}) // image
    Resource.raw_status({wrong_address}) // HTTP response code

     // Registering custom CSS resource (app-wide)
    Resource.register_external_css(url)
     // Registering custom JS resource (app-wide)
    Resource.register_external_js(string url)

     // You will provide Resources in the server in response to URLs
    function start(url) {
      match (url) {
        case {path:[] ... }: Resource.page("Home", home())
        case {~path ...}: Resource.page("Some page", gen_page(path))
      }
    }
    Server.start(Server.http, {dispatch: start})

[#See the full API#](/module/stdlib.core.web.resource/Resource)

//Other
//-----
//### Markdown
//[TODO]
//### Parsing
//[TODO]

External APIs
-------------
![^MongoDB (stdlib.apis.mongo)^]{/package/stdlib.apis.mongo}(/resources/img/api-mongodb.jpg)
![^CouchDB (stdlib.apis.couchdb)^]{/package/stdlib.apis.couchdb}(/resources/img/api-couchdb.jpg)
![^Dropbox (stdlib.apis.dropbox)^]{/package/stdlib.apis.dropbox}(/resources/img/api-dropbox.jpg)
![^Facebook (stdlib.apis.facebook)^]{/package/stdlib.apis.facebook}(/resources/img/api-facebook.jpg)
![^GitHub (stdlib.apis.github)^]{/package/stdlib.apis.github}(/resources/img/api-github.jpg)
![^Twitter (stdlib.apis.twitter)^]{/package/stdlib.apis.twitter}(/resources/img/api-twitter.jpg)
![^Google Maps (stdlib.apis.gmaps)^]{/package/stdlib.apis.gmaps}(/resources/img/api-google-maps.jpg)
![^IRC (stdlib.apis.irc)^]{/package/stdlib.apis.irc}(/resources/img/api-irc.jpg)
![^OAuth (stdlib.apis.oauth)^]{/package/stdlib.apis.oauth}(/resources/img/api-oauth.jpg)
![^RDF (stdlib.apis.rdf)^]{/package/stdlib.apis.rdf}(/resources/img/api-rdf.jpg)
![^ReCaptcha (stdlib.apis.recaptcha)^]{/package/stdlib.apis.recaptcha}(/resources/img/api-recaptcha.jpg)
![^World Weather Online (stdlib.apis.worldweather)^]{/package/stdlib.apis.worldweather}(/resources/img/api-worldweatheronline.jpg)
Something went wrong with that request. Please try again.