summaryrefslogtreecommitdiffhomepage
path: root/es-es/fsharp-es.html.markdown
blob: b7f80c449feb3ec37294a96f473c1bb82dc3eedb (plain)
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
---
language: F#
lang: es-es
contributors:
  - ['Scott Wlaschin', 'http://fsharpforfunandprofit.com/']
translators:
  - ['Angel Arciniega', 'https://github.com/AngelsProjects']
filename: learnfsharp-es.fs
---

F# es un lenguaje de programación funcional y orientado a objetos. Es gratis y su código fuente está abierto. Se ejecuta en Linux, Mac, Windows y más.

Tiene un poderoso sistema de tipado que atrapa muchos errores de tiempo de compilación, pero usa inferencias de tipados que le permiten ser leídos como un lenguaje dinámico.

La sintaxis de F# es diferente de los lenguajes que heredan de C.

- Las llaves no se usan para delimitar bloques de código. En cambio, se usa sangría (como en Python).
- Los espacios se usan para separar parámetros en lugar de comas.

Si quiere probar el siguiente código, puede ir a [tryfsharp.org](http://www.tryfsharp.org/Create) y pegarlo en [REPL](https://es.wikipedia.org/wiki/REPL).

```fsharp
// Los comentarios de una línea se escibren con una doble diagonal
(* Los comentarios multilínea usan parentesis (* . . . *)

-final del comentario multilínea- *)

// ================================================
// Syntaxis básica
// ================================================

// ------ "Variables" (pero no realmente) ------
// La palabra reservada "let" define un valor (inmutable)
let miEntero = 5
let miFlotante = 3.14
let miCadena = "hola"           // Tenga en cuenta que no es necesario ningún tipado

// ------ Listas ------
let dosACinco = [2;3;4;5]        // Los corchetes crean una lista con
                                 // punto y coma para delimitadores.
let unoACinco = 1 :: dosACinco   // :: Crea una lista con un nuevo elemento
// El resultado es [1;2;3;4;5]
let ceroACinco = [0;1] @ dosACinco   // @ Concatena dos listas

// IMPORTANTE: las comas no se usan para delimitar,
// solo punto y coma !

// ------ Funciones ------
// La palabra reservada "let" también define el nombre de una función.
let cuadrado x = x * x          // Tenga en cuenta que no se usa paréntesis.
cuadrado 3                      // Ahora, ejecutemos la función.
                              // De nuevo, sin paréntesis.

let agregar x y = x + y           // ¡No use add (x, y)! Eso significa
                              // algo completamente diferente.
agregar 2 3                       // Ahora, ejecutemos la función.

// Para definir una función en varias líneas, usemos la sangría.
// Los puntos y coma no son necesarios.
let pares lista =
   let esPar x = x%2 = 0     // Establece "esPar" como una función anidada
   List.filter esPar lista    // List.filter es una función de la biblioteca
                              // dos parámetros: una función que devuelve un
                              // booleano y una lista en la que trabajar

pares unoACinco               // Ahora, ejecutemos la función.

// Puedes usar paréntesis para aclarar.
// En este ejemplo, "map" se ejecuta primero, con dos argumentos,
// entonces "sum" se ejecuta en el resultado.
// Sin los paréntesis, "List.map" se pasará como argumento a List.sum.
let sumaDeCuadradosHasta100 =
   List.sum ( List.map cuadrado [1..100] )

// Puedes redirigir la salida de una función a otra con "|>"
// Redirigir datos es muy común en F#, como con los pipes de UNIX.

// Aquí está la misma función sumOfSquares escrita usando pipes
let sumaDeCuadradosHasta100piped =
   [1..100] |> List.map cuadrado |> List.sum  // "cuadrado" se declara antes

// Puede definir lambdas (funciones anónimas) gracias a la palabra clave "fun"
let sumaDeCuadradosHasta100ConFuncion =
   [1..100] |> List.map (fun x -> x*x) |> List.sum

// En F#, no hay palabra clave "return". Una función siempre regresa
// el valor de la última expresión utilizada.

// ------ Coincidencia de patrones ------
// Match..with .. es una sobrecarga de la condición de case/ switch.
let coincidenciaDePatronSimple =
   let x = "a"
   match x with
    | "a" -> printfn "x es a"
    | "b" -> printfn "x es b"
    | _ -> printfn "x es algo mas"   // guion bajo corresponde con todos los demás

// F# no permite valores nulos por defecto - debe usar el tipado de Option
// y luego coincide con el patrón.
// Some(..) y None son aproximadamente análogos a los envoltorios Nullable
let valorValido = Some(99)
let valorInvalido = None

// En este ejemplo, match..with encuentra una coincidencia con "Some" y "None",
// y muestra el valor de "Some" al mismo tiempo.
let coincidenciaDePatronDeOpciones entrada =
   match entrada with
    | Some i -> printfn "la entrada es un int=%d" i
    | None -> printfn "entrada faltante"

coincidenciaDePatronDeOpciones validValue
coincidenciaDePatronDeOpciones invalidValue

// ------ Viendo ------
// Las funciones printf/printfn son similares a las funciones
// Console.Write/WriteLine de C#.
printfn "Imprimiendo un int %i, a float %f, a bool %b" 1 2.0 true
printfn "Un string %s, y algo generico %A" "hola" [1;2;3;4]

// También hay funciones printf/sprintfn para formatear datos
// en cadena. Es similar al String.Format de C#.

// ================================================
// Mas sobre funciones
// ================================================

// F# es un verdadero lenguaje funcional - las funciones son
// entidades de primer nivel y se pueden combinar fácilmente
// para crear construcciones poderosas

// Los módulos se utilizan para agrupar funciones juntas.
// Se requiere sangría para cada módulo anidado.
module EjemploDeFuncion =

    // define una función de suma simple
    let agregar x y = x + y

    // uso básico de una función
    let a = agregar 1 2
    printfn "1+2 = %i" a

    // aplicación parcial para "hornear en" los parámetros (?)
    let agregar42 = agregar 42
    let b = agregar42 1
    printfn "42+1 = %i" b

    // composición para combinar funciones
    let agregar1 = agregar 1
    let agregar2 = agregar 2
    let agregar3 = agregar1 >> agregar2
    let c = agregar3 7
    printfn "3+7 = %i" c

    // funciones de primer nivel
    [1..10] |> List.map agregar3 |> printfn "la nueva lista es %A"

    // listas de funciones y más
    let agregar6 = [agregar1; agregar2; agregar3] |> List.reduce (>>)
    let d = agregar6 7
    printfn "1+2+3+7 = %i" d

// ================================================
// Lista de colecciones
// ================================================

// Il y a trois types de collection ordonnée :
// * Les listes sont les collections immutables les plus basiques
// * Les tableaux sont mutables et plus efficients
// * Les séquences sont lazy et infinies (e.g. un enumerator)
//
// Des autres collections incluent des maps immutables et des sets
// plus toutes les collections de .NET

module EjemplosDeLista =

    // las listas utilizan corchetes
    let lista1 = ["a";"b"]
    let lista2 = "c" :: lista1    // :: para una adición al principio
    let lista3 = lista1 @ lista2   // @ para la concatenación

    // Lista de comprensión (alias generadores)
    let cuadrados = [for i in 1..10 do yield i*i]

    //  Generador de números primos
    let rec tamiz = function
        | (p::xs) -> p :: tamiz [ for x in xs do if x % p > 0 then yield x ]
        | []      -> []
    let primos = tamiz [2..50]
    printfn "%A" primos

    // coincidencia de patrones para listas
    let listaDeCoincidencias unaLista =
        match unaLista with
        | [] -> printfn "la lista esta vacia"
        | [primero] -> printfn "la lista tiene un elemento %A " primero
        | [primero; segundo] -> printfn "la lista es %A y %A" primero segundo
        | _ -> printfn "la lista tiene mas de dos elementos"

    listaDeCoincidencias [1;2;3;4]
    listaDeCoincidencias [1;2]
    listaDeCoincidencias [1]
    listaDeCoincidencias []

    // Récursion en utilisant les listes
    let rec suma unaLista =
        match unaLista with
        | [] -> 0
        | x::xs -> x + suma xs
    suma [1..10]

    // -----------------------------------------
    // Funciones de la biblioteca estándar
    // -----------------------------------------

    // mapeo
    let agregar3 x = x + 3
    [1..10] |> List.map agregar3

    // filtrado
    let par x = x % 2 = 0
    [1..10] |> List.filter par

    // mucho más - consulte la documentación

module EjemploDeArreglo =

    // los arreglos usan corchetes con barras.
    let arreglo1 = [| "a";"b" |]
    let primero = arreglo1.[0]        // se accede al índice usando un punto

    // la coincidencia de patrones de los arreglos es la misma que la de las listas
    let coincidenciaDeArreglos una Lista =
        match unaLista with
        | [| |] -> printfn "la matriz esta vacia"
        | [| primero |] -> printfn "el arreglo tiene un elemento %A " primero
        | [| primero; second |] -> printfn "el arreglo es %A y %A" primero segundo
        | _ -> printfn "el arreglo tiene mas de dos elementos"

    coincidenciaDeArreglos [| 1;2;3;4 |]

    // La biblioteca estándar funciona como listas
    [| 1..10 |]
    |> Array.map (fun i -> i+3)
    |> Array.filter (fun i -> i%2 = 0)
    |> Array.iter (printfn "el valor es %i. ")

module EjemploDeSecuencia =

    // Las secuencias usan llaves
    let secuencia1 = seq { yield "a"; yield "b" }

    // Las secuencias pueden usar yield y
    // puede contener subsecuencias
    let extranio = seq {
        // "yield" agrega un elemento
        yield 1; yield 2;

        // "yield!" agrega una subsecuencia completa
        yield! [5..10]
        yield! seq {
            for i in 1..10 do
              if i%2 = 0 then yield i }}
    // prueba
    extranio |> Seq.toList

    // Las secuencias se pueden crear usando "unfold"
    // Esta es la secuencia de fibonacci
    let fib = Seq.unfold (fun (fst,snd) ->
        Some(fst + snd, (snd, fst + snd))) (0,1)

    // prueba
    let fib10 = fib |> Seq.take 10 |> Seq.toList
    printf "Los primeros 10 fib son %A" fib10

// ================================================
// Tipos de datos
// ================================================

module EejemploDeTipoDeDatos =

    // Todos los datos son inmutables por defecto

     // las tuplas son tipos anónimos simples y rápidos
     // - Usamos una coma para crear una tupla
    let dosTuplas = 1,2
    let tresTuplas = "a",2,true

    // Combinación de patrones para desempaquetar
    let x,y = dosTuplas  // asignado x=1 y=2

    // ------------------------------------
    // Los tipos de registro tienen campos con nombre
    // ------------------------------------

    // Usamos "type" con llaves para definir un tipo de registro
    type Persona = {Nombre:string; Apellido:string}

    // Usamos "let" con llaves para crear un registro
    let persona1 = {Nombre="John"; Apellido="Doe"}

    // Combinación de patrones para desempaquetar
    let {Nombre=nombre} = persona1    // asignado nombre="john"

    // ------------------------------------
    // Los tipos de unión (o variantes) tienen un conjunto de elección
     // Solo un caso puede ser válido a la vez.
    // ------------------------------------

    // Usamos "type" con barra/pipe para definir una unión estándar
    type Temp =
        | GradosC of float
        | GradosF of float

    // Una de estas opciones se usa para crear una
    let temp1 = GradosF 98.6
    let temp2 = GradosC 37.0

    // Coincidencia de patrón en todos los casos para desempaquetar (?)
    let imprimirTemp = function
       | GradosC t -> printfn "%f gradC" t
       | GradosF t -> printfn "%f gradF" t

    imprimirTemp temp1
    imprimirTemp temp2

    // ------------------------------------
    // Tipos recursivos
    // ------------------------------------

    // Los tipos se pueden combinar recursivamente de formas complejas
    // sin tener que crear subclases
    type Empleado =
      | Trabajador of Persona
      | Gerente of Empleado lista

    let jdoe = {Nombre="John";Apellido="Doe"}
    let trabajador = Trabajador jdoe

    // ------------------------------------
    // Modelado con tipados (?)
    // ------------------------------------

    // Los tipos de unión son excelentes para modelar el estado sin usar banderas (?)
    type DireccionDeCorreo =
        | DireccionDeCorreoValido of string
        | DireccionDeCorreoInvalido of string

    let intentarEnviarCorreo correoElectronico =
        match correoElectronico with // uso de patrones de coincidencia
        | DireccionDeCorreoValido direccion -> ()   // enviar
        | DireccionDeCorreoInvalido direccion -> () // no enviar

    // Combinar juntos, los tipos de unión y tipos de registro
     // ofrece una base excelente para el diseño impulsado por el dominio.
     // Puedes crear cientos de pequeños tipos que reflejarán fielmente
     // el dominio.

    type ArticuloDelCarrito = { CodigoDelProducto: string; Cantidad: int }
    type Pago = Pago of float
    type DatosActivosDelCarrito = { ArticulosSinPagar: ArticuloDelCarrito lista }
    type DatosPagadosDelCarrito = { ArticulosPagados: ArticuloDelCarrito lista; Pago: Pago}

    type CarritoDeCompras =
        | CarritoVacio  // sin datos
        | CarritoActivo of DatosActivosDelCarrito
        | CarritoPagado of DatosPagadosDelCarrito

    // ------------------------------------
    // Comportamiento nativo de los tipos
    // ------------------------------------

    // Los tipos nativos tienen el comportamiento más útil "listo para usar", sin ningún código para agregar.
     // * Inmutabilidad
     // * Bonita depuración de impresión
     // * Igualdad y comparación
     // * Serialización

     // La impresión bonita se usa con %A
    printfn "dosTuplas=%A,\nPersona=%A,\nTemp=%A,\nEmpleado=%A"
             dosTuplas persona1 temp1 trabajador

    // La igualdad y la comparación son innatas
     // Aquí hay un ejemplo con tarjetas.
    type JuegoDeCartas = Trebol | Diamante | Espada | Corazon
    type Rango = Dos | Tres | Cuatro | Cinco | Seis | Siete | Ocho
                | Nueve | Diez | Jack | Reina | Rey | As

    let mano = [ Trebol,As; Corazon,Tres; Corazon,As;
                 Espada,Jack; Diamante,Dos; Diamante,As ]

    // orden
    List.sort mano |> printfn "la mano ordenada es (de menos a mayor) %A"
    List.max mano |> printfn "la carta más alta es%A"
    List.min mano |> printfn "la carta más baja es %A"

// ================================================
// Patrones activos
// ================================================

module EjemplosDePatronesActivos =

    // F# tiene un tipo particular de coincidencia de patrón llamado "patrones activos"
    // donde el patrón puede ser analizado o detectado dinámicamente.

    // "clips de banana" es la sintaxis de los patrones activos

    // por ejemplo, definimos un patrón "activo" para que coincida con los tipos de "caracteres" ...
    let (|Digito|Latra|EspacioEnBlanco|Otros|) ch =
       if System.Char.IsDigit(ch) then Digito
       else if System.Char.IsLetter(ch) then Letra
       else if System.Char.IsWhiteSpace(ch) then EspacioEnBlanco
       else Otros

    // ... y luego lo usamos para hacer que la lógica de análisis sea más clara
    let ImprimirCaracter ch =
      match ch with
      | Digito -> printfn "%c es un Digito" ch
      | Letra -> printfn "%c es una Letra" ch
      | Whitespace -> printfn "%c es un Espacio en blanco" ch
      | _ -> printfn "%c es algo mas" ch

    // ver una lista
    ['a';'b';'1';' ';'-';'c'] |> List.iter ImprimirCaracter

    // -----------------------------------------
    // FizzBuzz usando patrones activos
    // -----------------------------------------

    // Puede crear un patrón de coincidencia parcial también
    // Solo usamos un guión bajo en la definición y devolvemos Some si coincide.
    let (|MultDe3|_|) i = if i % 3 = 0 then Some MultDe3 else None
    let (|MultDe5|_|) i = if i % 5 = 0 then Some MultDe5 else None

    // la función principal
    let fizzBuzz i =
      match i with
      | MultDe3 & MultDe5 -> printf "FizzBuzz, "
      | MultDe3 -> printf "Fizz, "
      | MultDe5 -> printf "Buzz, "
      | _ -> printf "%i, " i

    // prueba
    [1..20] |> List.iter fizzBuzz

// ================================================
// concisión
// ================================================

module EjemploDeAlgoritmo =

    // F# tiene una alta relación señal / ruido, lo que permite leer el código
    // casi como un algoritmo real

    // ------ Ejemplo: definir una función sumaDeCuadrados ------
    let sumaDeCuadrados n =
       [1..n]                   // 1) Tome todos los números del 1 al n
       |> List.map cuadrado     // 2) Elevar cada uno de ellos al cuadrado
       |> List.sum              // 3) Realiza su suma

    // prueba
    sumaDeCuadrados 100 |> printfn "Suma de cuadrados = %A"

    // ------ Ejemplo: definir una función de ordenación ------
    let rec ordenar lista =
       match lista with
       // Si la lista está vacía
       | [] ->
            []                              // devolvemos una lista vacía
       // si la lista no está vacía
       | primerElemento::otrosElementos ->  // tomamos el primer elemento
            let elementosMasPequenios =     // extraemos los elementos más pequeños
                otrosElementos              // tomamos el resto
                |> List.filter (fun e -> e < primerElemento)
                |> ordenar                  // y los ordenamos
            let elementosMasGrandes =       // extraemos el mas grande
                otrosElementos              // de los que permanecen
                |> List.filter (fun e -> e >= primerElemento)
                |> ordenar                  // y los ordenamos
            // Combinamos las 3 piezas en una nueva lista que devolvemos
            List.concat [elementosMasPequenios; [primerElemento]; elementosMasGrandes]

    // prueba
    ordenar [1;5;23;18;9;1;3] |> printfn "Ordenado = %A"

// ================================================
// Código asíncrono
// ================================================

module AsyncExample =

    // F# incluye características para ayudar con el código asíncrono
    // sin conocer la "pirámide del destino"
    //
    // El siguiente ejemplo descarga una secuencia de página web en paralelo.

    open System.Net
    open System
    open System.IO
    open Microsoft.FSharp.Control.CommonExtensions

    // Recuperar el contenido de una URL de forma asincrónica
    let extraerUrlAsync url =
        async {   // La palabra clave "async" y llaves
                  // crear un objeto "asincrónico"
            let solicitud = WebRequest.Create(Uri(url))
            use! respuesta = solicitud.AsyncGetResponse()
                // use! es una tarea asincrónica
            use flujoDeDatos = resp.GetResponseStream()
                // "use" dispara automáticamente la funcion close()
                // en los recursos al final de las llaves
            use lector = new IO.StreamReader(flujoDeDatos)
            let html = lector.ReadToEnd()
            printfn "terminó la descarga %s" url
            }

    // una lista de sitios para informar
    let sitios = ["http://www.bing.com";
                 "http://www.google.com";
                 "http://www.microsoft.com";
                 "http://www.amazon.com";
                 "http://www.yahoo.com"]

    // ¡Aqui vamos!
    sitios
    |> List.map extraerUrlAsync // crear una lista de tareas asíncrona
    |> Async.Parallel           // decirle a las tareas que se desarrollan en paralelo
    |> Async.RunSynchronously   // ¡Empieza!

// ================================================
// Compatibilidad .NET
// ================================================

module EjemploCompatibilidadNet =

    // F# puede hacer casi cualquier cosa que C# pueda hacer, y se ajusta
    // perfectamente con bibliotecas .NET o Mono.

    // ------- Trabaja con las funciones de las bibliotecas existentes  -------

    let (i1success,i1) = System.Int32.TryParse("123");
    if i1success then printfn "convertido como %i" i1 else printfn "conversion fallida"

    // ------- Implementar interfaces sobre la marcha! -------

    // Crea un nuevo objeto que implemente IDisposable
    let crearRecurso name =
       { new System.IDisposable
         with member this.Dispose() = printfn "%s creado" name }

    let utilizarYDisponerDeRecursos =
        use r1 = crearRecurso "primer recurso"
        printfn "usando primer recurso"
        for i in [1..3] do
            let nombreDelRecurso = sprintf "\tinner resource %d" i
            use temp = crearRecurso nombreDelRecurso
            printfn "\thacer algo con %s" nombreDelRecurso
        use r2 = crearRecurso "segundo recurso"
        printfn "usando segundo recurso"
        printfn "hecho."

    // ------- Código orientado a objetos -------

    // F# es también un verdadero lenguaje OO.
    // Admite clases, herencia, métodos virtuales, etc.

    // interfaz de tipo genérico
    type IEnumerator<'a> =
        abstract member Actual : 'a
        abstract MoverSiguiente : unit -> bool

    // Clase base abstracta con métodos virtuales
    [<AbstractClass>]
    type Figura() =
        // propiedades de solo lectura
        abstract member Ancho : int with get
        abstract member Alto : int with get
        // método no virtual
        member this.AreaDelimitadora = this.Alto * this.Ancho
        // método virtual con implementación de la clase base
        abstract member Imprimir : unit -> unit
        default this.Imprimir () = printfn "Soy una Figura"

    // clase concreta que hereda de su clase base y sobrecarga
    type Rectangulo(x:int, y:int) =
        inherit Figura()
        override this.Ancho = x
        override this.Alto = y
        override this.Imprimir ()  = printfn "Soy un Rectangulo"

    // prueba
    let r = Rectangulo(2,3)
    printfn "La anchura es %i" r.Ancho
    printfn "El area es %i" r.AreaDelimitadora
    r.Imprimir()

    // ------- extensión de método  -------

    // Al igual que en C#, F# puede extender las clases existentes con extensiones de método.
    type System.String with
       member this.EmpiezaConA = this.EmpiezaCon "A"

    // prueba
    let s = "Alice"
    printfn "'%s' empieza con una 'A' = %A" s s.EmpiezaConA

    // ------- eventos -------

    type MiBoton() =
        let eventoClick = new Event<_>()

        [<CLIEvent>]
        member this.AlHacerClick = eventoClick.Publish

        member this.PruebaEvento(arg) =
            eventoClick.Trigger(this, arg)

    // prueba
    let miBoton = new MiBoton()
    miBoton.AlHacerClick.Add(fun (sender, arg) ->
            printfn "Haga clic en el evento con arg=%O" arg)

    miBoton.PruebaEvento("Hola Mundo!")
```

## Más información

Para más demostraciones de F#, visite el sitio [Try F#](http://www.tryfsharp.org/Learn), o sigue la serie [why use F#](http://fsharpforfunandprofit.com/why-use-fsharp/).

Aprenda más sobre F# en [fsharp.org](http://fsharp.org/).